This commit is contained in:
Vladimir Dubovik 2025-02-19 12:43:52 +03:00
parent 9f717d83df
commit bb268cc6ad
31 changed files with 335 additions and 95 deletions

View File

@ -0,0 +1,44 @@
//
// LoadingScheduleView.swift
// Schedule ICTIS
//
// Created by G412 on 19.02.2025.
//
import SwiftUI
struct LoadingScheduleView: View {
@State private var isAnimated = false
var body: some View {
ZStack {
ScrollView(.vertical, showsIndicators: false) {
VStack(spacing: 20) {
ForEach(0..<3, id: \.self) { _ in
RoundedRectangle(cornerRadius: 20)
.fill(
LinearGradient(
gradient: Gradient(colors: [
isAnimated ? Color.gray.opacity(0.5) : Color.gray.opacity(0.2),
isAnimated ? Color.gray.opacity(0.2) : Color.gray.opacity(0.5)
]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.frame(height: 65)
.padding(.horizontal, 20)
.animation(.linear(duration: 0.9).repeatForever(autoreverses: true), value: isAnimated)
}
}
.onAppear {
isAnimated.toggle()
}
.padding(.top, 30)
}
}
}
}
#Preview {
LoadingScheduleView()
}

View File

@ -2,7 +2,7 @@
// LoadingView.swift // LoadingView.swift
// Schedule ICTIS // Schedule ICTIS
// //
// Created by G412 on 11.12.2024. // Created by Mironov Egor on 11.12.2024.
// //
import SwiftUI import SwiftUI

View File

@ -2,7 +2,7 @@
// CreatedClassView.swift // CreatedClassView.swift
// Schedule ICTIS // Schedule ICTIS
// //
// Created by G412 on 23.12.2024. // Created by Mironov Egor on 23.12.2024.
// //
import SwiftUI import SwiftUI
@ -12,7 +12,7 @@ struct CreatedClassView: View {
var provider = ClassProvider.shared var provider = ClassProvider.shared
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 let check = existingCopy { if existingCopy != nil {
HStack(spacing: 10) { HStack(spacing: 10) {
VStack { VStack {
Text(getTimeString(_class.starttime)) Text(getTimeString(_class.starttime))
@ -20,6 +20,7 @@ struct CreatedClassView: View {
Text(getTimeString(_class.endtime)) Text(getTimeString(_class.endtime))
.font(.custom("Montserrat-Regular", size: 15)) .font(.custom("Montserrat-Regular", size: 15))
} }
.frame(width: 48)
.padding(.top, 7) .padding(.top, 7)
.padding(.bottom, 7) .padding(.bottom, 7)
.padding(.leading, 10) .padding(.leading, 10)
@ -30,7 +31,7 @@ struct CreatedClassView: View {
.padding(.bottom, 7) .padding(.bottom, 7)
.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-Regular", size: 18)) .font(.custom("Montserrat-Medium", size: 15))
.padding(.top, 7) .padding(.top, 7)
.padding(.bottom, 7) .padding(.bottom, 7)
Spacer() Spacer()

View File

@ -18,7 +18,7 @@ struct AuditoryFieldView: View {
.padding(.leading, 12) .padding(.leading, 12)
.padding(.trailing, 14) .padding(.trailing, 14)
TextField(labelForField, text: $text) TextField(labelForField, text: $text)
.font(.custom("Montserrat-Regular", size: 18)) .font(.custom("Montserrat-Meduim", size: 17))
.disableAutocorrection(true) .disableAutocorrection(true)
.submitLabel(.done) .submitLabel(.done)
.focused($isFocused) .focused($isFocused)

View File

@ -14,7 +14,7 @@ struct CommentFieldView: View {
var body: some View { var body: some View {
HStack { HStack {
TextField("Комментарий", text: $textForComment) TextField("Комментарий", text: $textForComment)
.font(.custom("Montserrat-Regular", size: 18)) .font(.custom("Montserrat-Medium", size: 17))
.submitLabel(.done) .submitLabel(.done)
.multilineTextAlignment(.leading) .multilineTextAlignment(.leading)
.focused($isFocused) .focused($isFocused)

View File

@ -18,7 +18,7 @@ struct ProfessorFieldView: View {
.padding(.leading, 12) .padding(.leading, 12)
.padding(.trailing, 7) .padding(.trailing, 7)
TextField(labelForField, text: $text) TextField(labelForField, text: $text)
.font(.custom("Montserrat-Regular", size: 18)) .font(.custom("Montserrat-Meduim", size: 17))
.disableAutocorrection(true) .disableAutocorrection(true)
.submitLabel(.done) .submitLabel(.done)
.focused($isFocused) .focused($isFocused)

View File

@ -23,7 +23,7 @@ struct StartEndTimeFieldView: View {
if !isTimeSelected || isIncorrectDate { if !isTimeSelected || isIncorrectDate {
Text(text) Text(text)
.font(.custom("Montserrat-Regular", size: 17)) .font(.custom("Montserrat-Meduim", size: 17))
.foregroundColor(.gray.opacity(0.5)) .foregroundColor(.gray.opacity(0.5))
} }
else { else {

View File

@ -19,7 +19,7 @@ struct SubjectFieldView: View {
.padding(.leading, 12) .padding(.leading, 12)
.padding(.trailing, 9) .padding(.trailing, 9)
TextField(labelForField, text: $text) TextField(labelForField, text: $text)
.font(.custom("Montserrat-Regular", size: 18)) .font(.custom("Montserrat-Meduim", size: 17))
.disableAutocorrection(true) .disableAutocorrection(true)
.submitLabel(.done) .submitLabel(.done)
.focused($isFocused) .focused($isFocused)
@ -33,10 +33,10 @@ struct SubjectFieldView: View {
Group { Group {
if isShowingSubjectFieldRed { if isShowingSubjectFieldRed {
Text("Поле должно быть заполнено!") Text("Поле должно быть заполнено!")
.font(.custom("Montserrat-Regular", size: 18)) .font(.custom("Montserrat-Meduim", size: 17))
.foregroundColor(.red) .foregroundColor(.red)
.frame(width: 290) .frame(width: 290)
.padding(.leading, -42) .padding(.leading, -38)
} }
} }
} }

View File

@ -2,7 +2,7 @@
// FirstLaunchScheduleView.swift // FirstLaunchScheduleView.swift
// Schedule ICTIS // Schedule ICTIS
// //
// Created by G412 on 06.12.2024. // Created by Mironov Egor on 06.12.2024.
// //
import SwiftUI import SwiftUI

View File

@ -24,27 +24,25 @@ struct MainView: View {
} }
CurrentDateView() CurrentDateView()
if vm.isLoading { if vm.isLoading {
LoadingView(isLoading: $vm.isLoading) LoadingScheduleView()
} }
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
Button("ОК") {
print("This alert")
vm.isShowingAlertForIncorrectGroup = false
vm.errorInNetwork = nil
}
} message: { error in } message: { error in
Text(error.failureReason) Text(error.failureReason)
} }
.background(Color("background")) .background(Color("background"))
.onTapGesture {
isFocusedSearchBar = false
}
.onAppear {
vm.group = UserDefaults.standard.string(forKey: "group") ?? "notSeted"
if vm.group != "notSeted" {
}
}
} }
@ViewBuilder @ViewBuilder
@ -53,14 +51,14 @@ struct MainView: View {
HStack { HStack {
VStack (alignment: .leading, spacing: 0) { VStack (alignment: .leading, spacing: 0) {
Text(vm.selectedDay.format("EEEE")) Text(vm.selectedDay.format("EEEE"))
.font(.custom("Montserrat-SemiBold", size: 40)) .font(.custom("Montserrat-SemiBold", size: 30))
.foregroundStyle(.black) .foregroundStyle(.black)
HStack (spacing: 5) { HStack (spacing: 5) {
Text(vm.selectedDay.format("dd")) Text(vm.selectedDay.format("dd"))
.font(.custom("Montserrat-Bold", size: 20)) .font(.custom("Montserrat-Bold", size: 17))
.foregroundStyle(Color("grayForDate")) .foregroundStyle(Color("grayForDate"))
Text(vm.selectedDay.format("MMMM")) Text(vm.selectedDay.format("MMMM"))
.font(.custom("Montserrat-Bold", size: 20)) .font(.custom("Montserrat-Bold", size: 17))
.foregroundStyle(Color("grayForDate")) .foregroundStyle(Color("grayForDate"))
Spacer() Spacer()
Button(action: { Button(action: {
@ -70,7 +68,7 @@ struct MainView: View {
}) { }) {
HStack(spacing: 2) { HStack(spacing: 2) {
Text(isShowingMonthSlider ? "Свернуть" : "Развернуть") Text(isShowingMonthSlider ? "Свернуть" : "Развернуть")
.font(.custom("Montserrat-Light", size: 16)) .font(.custom("Montserrat-Regular", size: 15))
.foregroundStyle(Color.blue) .foregroundStyle(Color.blue)
Image(isShowingMonthSlider ? "arrowup" : "arrowdown") Image(isShowingMonthSlider ? "arrowup" : "arrowdown")
.resizable() .resizable()

View File

@ -2,7 +2,7 @@
// NoScheduleView.swift // NoScheduleView.swift
// Schedule ICTIS // Schedule ICTIS
// //
// Created by G412 on 12.12.2024. // Created by Mironov Egor on 12.12.2024.
// //
import SwiftUI import SwiftUI
@ -11,9 +11,9 @@ struct NoScheduleView: View {
var body: some View { var body: some View {
VStack { VStack {
ScrollView (showsIndicators: false) { ScrollView (showsIndicators: false) {
Text("Пока расписания нет") Text("Пока что расписания нет😪")
.padding(.top, 20) .padding(.top, 100)
.font(.custom("Montserrat-Regular", size: 15)) .font(.custom("Montserrat-SemiBold", size: 17))
} }
} }
} }

View File

@ -17,7 +17,7 @@ struct ScheduleView: View {
var provider = ClassProvider.shared var provider = ClassProvider.shared
var body: some View { var body: some View {
if vm.isLoading { if vm.isLoading {
LoadingView(isLoading: $vm.isLoading) LoadingScheduleView()
} }
else { else {
if vm.errorInNetwork != .invalidResponse { if vm.errorInNetwork != .invalidResponse {
@ -33,12 +33,13 @@ struct ScheduleView: View {
HStack(spacing: 10) { HStack(spacing: 10) {
VStack { VStack {
Text(convertTimeString(vm.classes[1][lessonIndex])[0]) Text(convertTimeString(vm.classes[1][lessonIndex])[0])
.font(.custom("Montserrat-Medium", size: 15)) .font(.custom("Montserrat-Regular", size: 15))
.padding(.bottom, 1) .padding(.bottom, 1)
Text(convertTimeString(vm.classes[1][lessonIndex])[1]) Text(convertTimeString(vm.classes[1][lessonIndex])[1])
.font(.custom("Montserrat-Medium", size: 15)) .font(.custom("Montserrat-Regular", size: 15))
.padding(.top, 1) .padding(.top, 1)
} }
.frame(width: 48)
.padding(.top, 7) .padding(.top, 7)
.padding(.bottom, 7) .padding(.bottom, 7)
.padding(.leading, 10) .padding(.leading, 10)
@ -49,7 +50,7 @@ struct ScheduleView: View {
.padding(.bottom, 7) .padding(.bottom, 7)
.foregroundColor(getColorForClass(lesson)) .foregroundColor(getColorForClass(lesson))
Text(lesson) Text(lesson)
.font(.custom("Montserrat-Regular", size: 17)) .font(.custom("Montserrat-Medium", size: 15))
.lineSpacing(3) .lineSpacing(3)
.padding(.top, 9) .padding(.top, 9)
.padding(.bottom, 9) .padding(.bottom, 9)

View File

@ -28,8 +28,8 @@ 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)
vm.group = text
} }
self.text = "" self.text = ""
} }
@ -53,7 +53,7 @@ struct SearchBarView: View {
RoundedRectangle(cornerRadius: 10) RoundedRectangle(cornerRadius: 10)
.fill(.white) .fill(.white)
) )
if (!vm.isFirstStartOffApp && !isFocused) { if !isFocused {
Button { Button {
isShowingSheet = true isShowingSheet = true
} label: { } label: {

View File

@ -76,11 +76,11 @@ struct CreateEditClassView: View {
.padding(.trailing, 5) .padding(.trailing, 5)
Text("Дата") Text("Дата")
.foregroundColor(Color("grayForFields").opacity(0.5)) .foregroundColor(Color("grayForFields").opacity(0.5))
.font(.custom("Montserrat-Regular", size: 18)) .font(.custom("Montserrat-Meduim", size: 17))
Spacer() Spacer()
Text("\(vm._class.day, formatter: dateFormatter)") Text("\(vm._class.day, formatter: dateFormatter)")
.foregroundColor(.black) .foregroundColor(.black)
.font(.custom("Montserrat-Medium", size: 18)) .font(.custom("Montserrat-Medium", size: 17))
.padding(.trailing, 20) .padding(.trailing, 20)
} }
.frame(height: 40) .frame(height: 40)

View File

@ -18,7 +18,7 @@ struct MonthTabView: View {
HStack (spacing: 34) { HStack (spacing: 34) {
ForEach(MockData.daysOfWeek.indices, id: \.self) { index in ForEach(MockData.daysOfWeek.indices, id: \.self) { index in
Text(MockData.daysOfWeek[index]) Text(MockData.daysOfWeek[index])
.font(.custom("Montserrat-SemiBold", size: 15)) .font(.custom("Montserrat-SemiBold", size: 14))
.foregroundColor(MockData.daysOfWeek[index] == "Вс" ? Color(.red) : Color("customGray2")) .foregroundColor(MockData.daysOfWeek[index] == "Вс" ? Color(.red) : Color("customGray2"))
.padding(.top, 13) .padding(.top, 13)
.foregroundColor(.gray) .foregroundColor(.gray)

View File

@ -16,7 +16,7 @@ struct WeekViewForMonth: View {
ForEach(week) { day in ForEach(week) { day in
VStack { VStack {
Text(day.date.format("dd")) Text(day.date.format("dd"))
.font(.custom("Montserrat-Medium", size: 15)) .font(.custom("Montserrat-SemiBold", size: 14))
.foregroundStyle(getForegroundColor(day: day)) .foregroundStyle(getForegroundColor(day: day))
} }
.frame(width: 30, height: 30, alignment: .center) .frame(width: 30, height: 30, alignment: .center)

View File

@ -18,12 +18,12 @@ struct WeekViewForWeek: View {
ForEach(week) { day in ForEach(week) { day in
VStack (spacing: 1) { VStack (spacing: 1) {
Text(day.date.format("E")) Text(day.date.format("E"))
.font(.custom("Montserrat-Medium", size: 16)) .font(.custom("Montserrat-SemiBold", size: 14))
.foregroundColor(day.date.format("E") == "Вс" ? Color(.red) : isSameDate(day.date, vm.selectedDay) ? Color("customGray1") : Color("customGray3")) .foregroundColor(day.date.format("E") == "Вс" ? Color(.red) : isSameDate(day.date, vm.selectedDay) ? Color("customGray1") : Color("customGray3"))
.padding(.top, 13) .padding(.top, 13)
.foregroundColor(.gray) .foregroundColor(.gray)
Text(day.date.format("dd")) Text(day.date.format("dd"))
.font(.custom("Montserrat-Medium", size: 15)) .font(.custom("Montserrat-Semibold", size: 14))
.foregroundStyle(isSameDate(day.date, vm.selectedDay) ? .white : .black) .foregroundStyle(isSameDate(day.date, vm.selectedDay) ? .white : .black)
.padding(.bottom, 13) .padding(.bottom, 13)
} }

View File

@ -2,7 +2,7 @@
// MockData.swift // MockData.swift
// Schedule ICTIS // Schedule ICTIS
// //
// Created by G412 on 06.12.2024. // Created by Mironov Egor on 06.12.2024.
// //
import Foundation import Foundation
@ -19,4 +19,6 @@ struct MockData {
static let themes = ["Светлая", "Темная", "Системная"] static let themes = ["Светлая", "Темная", "Системная"]
static let languages = ["Русский", "Английский", "Китайский", "Испанский"] static let languages = ["Русский", "Английский", "Китайский", "Испанский"]
static let groups = ["КТбо2-6", "КТбо1-9", "КТбо3-3", "ВУЦ", "КТао1-1", "КТсо2-2"]
} }

View File

@ -0,0 +1,20 @@
//
// SubjectsModel.swift
// Schedule ICTIS
//
// Created by Mironov Egor on 19.02.2025.
//
import Foundation
// MARK: - Welcome
struct Welcome: Decodable {
let choices: [Choice]
}
// MARK: - Choice
struct Choice: Decodable, Identifiable {
let name: String
let id: String
let group: String
}

View File

@ -1,7 +1,7 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "arrow.png", "filename" : "arrowdown.svg",
"idiom" : "universal", "idiom" : "universal",
"scale" : "1x" "scale" : "1x"
}, },

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 B

View File

@ -0,0 +1,3 @@
<svg width="14" height="8" viewBox="0 0 14 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13 1L7 7L1 1" stroke="#007AFF" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 191 B

View File

@ -1,7 +1,7 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "Vector.png", "filename" : "arrowup.svg",
"idiom" : "universal", "idiom" : "universal",
"scale" : "1x" "scale" : "1x"
}, },

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 B

View File

@ -0,0 +1,3 @@
<svg width="14" height="8" viewBox="0 0 14 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 7L7 1L13 7" stroke="#007AFF" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 191 B

View File

@ -2,7 +2,7 @@
// SelectedGroupView.swift // SelectedGroupView.swift
// Schedule ICTIS // Schedule ICTIS
// //
// Created by G412 on 30.01.2025. // Created by Mironov Egor on 30.01.2025.
// //
import SwiftUI import SwiftUI
@ -11,7 +11,9 @@ struct SelectingGroupView: View {
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
@FocusState private var isFocused: Bool @FocusState private var isFocused: Bool
@State private var text: String = "" @State private var text: String = ""
@Binding var group: String @ObservedObject var vm: ScheduleViewModel
@State private var isLoading = false
@State private var searchTask: DispatchWorkItem?
var body: some View { var body: some View {
NavigationView { NavigationView {
VStack { VStack {
@ -23,14 +25,37 @@ struct SelectingGroupView: View {
TextField("Поиск группы", text: $text) TextField("Поиск группы", text: $text)
.disableAutocorrection(true) .disableAutocorrection(true)
.focused($isFocused) .focused($isFocused)
.onChange(of: text) { oldValue, newValue in
searchTask?.cancel()
let task = DispatchWorkItem {
if !text.isEmpty {
vm.fetchGroups(group: text)
}
}
searchTask = task
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: task)
}
.onSubmit { .onSubmit {
self.isFocused = false self.isFocused = false
if (!text.isEmpty) { if (!text.isEmpty) {
UserDefaults.standard.set(text, forKey: "group") vm.fetchWeekSchedule(group: text)
group = text self.isLoading = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.isLoading = false
if vm.errorInNetwork == .noError {
vm.errorInNetwork = nil
print("Зашел")
UserDefaults.standard.set(text, forKey: "group")
vm.group = text
self.text = ""
dismiss()
}
else {
vm.isShowingAlertForIncorrectGroup = true
vm.errorInNetwork = .invalidResponse
}
}
} }
self.text = ""
dismiss()
} }
.submitLabel(.done) .submitLabel(.done)
if isFocused { if isFocused {
@ -52,17 +77,45 @@ struct SelectingGroupView: View {
RoundedRectangle(cornerRadius: 15) RoundedRectangle(cornerRadius: 15)
.fill(.white) .fill(.white)
) )
.padding(.horizontal, 10)
Spacer() Spacer()
if isLoading {
LoadingView(isLoading: $isLoading)
}
if isFocused {
ScrollView(.vertical, showsIndicators: true) {
ForEach(vm.groups) { item in
VStack {
Rectangle()
.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(.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()
}
}
}
}
}
} }
.padding(.horizontal, 10)
.background(Color("background")) .background(Color("background"))
.onTapGesture { }
self.isFocused = false .onAppear {
} vm.fetchGroups(group: "кт")
} }
} }
} }
#Preview {
SelectingGroupView(group: .constant("КТбо2-6"))
}

View File

@ -2,14 +2,18 @@
// SelectedVPKView.swift // SelectedVPKView.swift
// Schedule ICTIS // Schedule ICTIS
// //
// Created by G412 on 30.01.2025. // Created by Mironov Egor on 30.01.2025.
// //
import SwiftUI import SwiftUI
struct SelectingVPKView: View { struct SelectingVPKView: View {
@Environment(\.dismiss) private var dismiss
@FocusState private var isFocused: Bool @FocusState private var isFocused: Bool
@State private var text: String = "" @State private var text: String = ""
@ObservedObject var vm: ScheduleViewModel
@State private var isLoading = false
@State private var searchTask: DispatchWorkItem?
var body: some View { var body: some View {
NavigationView { NavigationView {
VStack { VStack {
@ -21,12 +25,37 @@ struct SelectingVPKView: View {
TextField("Поиск ВПК", text: $text) TextField("Поиск ВПК", text: $text)
.disableAutocorrection(true) .disableAutocorrection(true)
.focused($isFocused) .focused($isFocused)
.onChange(of: text) { oldValue, newValue in
searchTask?.cancel()
let task = DispatchWorkItem {
if !text.isEmpty {
vm.fetchGroups(group: text)
}
}
searchTask = task
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: task)
}
.onSubmit { .onSubmit {
self.isFocused = false self.isFocused = false
if (!text.isEmpty) { if (!text.isEmpty) {
UserDefaults.standard.set(text, forKey: "group") vm.fetchWeekSchedule(group: text)
self.isLoading = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.isLoading = false
if vm.errorInNetwork == .noError {
vm.errorInNetwork = nil
print("Зашел")
UserDefaults.standard.set(text, forKey: "vpk")
vm.group = text
self.text = ""
dismiss()
}
else {
vm.isShowingAlertForIncorrectGroup = true
vm.errorInNetwork = .invalidResponse
}
}
} }
self.text = ""
} }
.submitLabel(.done) .submitLabel(.done)
if isFocused { if isFocused {
@ -48,17 +77,45 @@ struct SelectingVPKView: View {
RoundedRectangle(cornerRadius: 15) RoundedRectangle(cornerRadius: 15)
.fill(.white) .fill(.white)
) )
.padding(.horizontal, 10)
Spacer() Spacer()
if isLoading {
LoadingView(isLoading: $isLoading)
}
if isFocused {
ScrollView(.vertical, showsIndicators: true) {
ForEach(vm.groups) { item in
VStack {
Rectangle()
.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(.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.group = item.name
vm.fetchWeekSchedule(group: item.name)
dismiss()
}
}
}
}
}
} }
.padding(.horizontal, 10)
.background(Color("background")) .background(Color("background"))
.onTapGesture { }
self.isFocused = false .onAppear {
} vm.fetchGroups(group: "впк")
} }
} }
} }
#Preview {
SelectingVPKView()
}

View File

@ -2,7 +2,7 @@
// SettingsView.swift // SettingsView.swift
// Schedule ICTIS // Schedule ICTIS
// //
// Created by G412 on 30.01.2025. // Created by Mironov Egor on 30.01.2025.
// //
import SwiftUI import SwiftUI
@ -11,6 +11,8 @@ struct SettingsView: View {
@ObservedObject var vm: ScheduleViewModel @ObservedObject var vm: ScheduleViewModel
@State private var selectedTheme = "Светлая" @State private var selectedTheme = "Светлая"
@State private var selectedLanguage = "Русский" @State private var selectedLanguage = "Русский"
@AppStorage("group") private var favGroup = ""
@AppStorage("vpk") private var favVPK = ""
var body: some View { var body: some View {
NavigationView { NavigationView {
VStack { VStack {
@ -28,17 +30,18 @@ struct SettingsView: View {
}) })
} }
Section("Расписание") { Section("Расписание") {
NavigationLink(destination: SelectingGroupView(group: $vm.group)) { NavigationLink(destination: SelectingGroupView(vm: vm)) {
LabeledContent { LabeledContent {
Text(vm.group) Text(favGroup)
} label: { } label: {
Text("Избранное расписание") Text("Избранное расписание")
} }
} }
NavigationLink(destination: SelectingVPKView()) { NavigationLink(destination: SelectingVPKView(vm: vm)) {
LabeledContent { LabeledContent {
Text(favVPK)
} label: { } label: {
Text("ВПК") Text("Избранное ВПК")
} }
} }
} }

View File

@ -185,9 +185,11 @@ extension WeekViewForWeek {
let calendar = Calendar.current let calendar = Calendar.current
if weekSlider.indices.contains(currentWeekIndex) { if weekSlider.indices.contains(currentWeekIndex) {
if let firstDate = weekSlider[currentWeekIndex].first?.date, if let firstDate = weekSlider[currentWeekIndex].first?.date,
currentWeekIndex == 0 { currentWeekIndex == 0 {
vm.week -= 1 vm.week -= 1
vm.fetchWeekSchedule(isOtherWeek: true) if vm.group != "" {
vm.fetchWeekSchedule(isOtherWeek: true)
}
weekSlider.insert(firstDate.createPrevioustWeek(), at: 0) weekSlider.insert(firstDate.createPrevioustWeek(), at: 0)
weekSlider.removeLast() weekSlider.removeLast()
currentWeekIndex = 1 currentWeekIndex = 1
@ -196,9 +198,11 @@ 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
vm.fetchWeekSchedule(isOtherWeek: true) if vm.group != "" {
vm.fetchWeekSchedule(isOtherWeek: true)
}
weekSlider.append(lastDate.createNextWeek()) weekSlider.append(lastDate.createNextWeek())
weekSlider.removeFirst() weekSlider.removeFirst()
currentWeekIndex = weekSlider.count - 2 currentWeekIndex = weekSlider.count - 2
@ -242,8 +246,10 @@ extension WeekViewForMonth {
} }
print(difBetweenWeeks) print(difBetweenWeeks)
vm.week += difBetweenWeeks vm.week += difBetweenWeeks
vm.fetchWeekSchedule(isOtherWeek: true) if vm.group != "" {
vm.fetchWeekSchedule(isOtherWeek: true)
} }
}
vm.selectedDay = day.date vm.selectedDay = day.date
vm.updateSelectedDayIndex() vm.updateSelectedDayIndex()
} }
@ -271,25 +277,29 @@ extension MonthTabView {
let calendar = Calendar.current let calendar = Calendar.current
if monthSlider.indices.contains(currentMonthIndex) { if monthSlider.indices.contains(currentMonthIndex) {
if let firstDate = monthSlider[currentMonthIndex].first?.week[0].date, if let firstDate = monthSlider[currentMonthIndex].first?.week[0].date,
currentMonthIndex == 0 { currentMonthIndex == 0 {
monthSlider.insert(firstDate.createPreviousMonth(), at: 0) monthSlider.insert(firstDate.createPreviousMonth(), at: 0)
monthSlider.removeLast() monthSlider.removeLast()
currentMonthIndex = 1 currentMonthIndex = 1
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
vm.fetchWeekSchedule(isOtherWeek: true) if vm.group != "" {
vm.fetchWeekSchedule(isOtherWeek: true)
}
} }
if let lastDate = monthSlider[currentMonthIndex].last?.week[6].date, if let lastDate = monthSlider[currentMonthIndex].last?.week[6].date,
currentMonthIndex == (monthSlider.count - 1) { currentMonthIndex == (monthSlider.count - 1) {
monthSlider.append(lastDate.createNextMonth()) monthSlider.append(lastDate.createNextMonth())
monthSlider.removeFirst() monthSlider.removeFirst()
currentMonthIndex = monthSlider.count - 2 currentMonthIndex = monthSlider.count - 2
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
vm.fetchWeekSchedule(isOtherWeek: true) if vm.group != "" {
vm.fetchWeekSchedule(isOtherWeek: true)
}
} }
} }
} }

View File

@ -31,9 +31,9 @@ final class NetworkManager {
func getSchedule(_ group: String) async throws -> Schedule { func getSchedule(_ group: String) async throws -> Schedule {
let newUrlForGroup = makeUrlForGroup(group) let newUrlForGroup = makeUrlForGroup(group)
guard let url = URL(string: newUrlForGroup) else {throw NetworkError.invalidUrl} guard let url = URL(string: newUrlForGroup) else { throw NetworkError.invalidUrl }
let (data, response) = try await URLSession.shared.data(from: url) let (data, response) = try await URLSession.shared.data(from: url)
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {throw NetworkError.invalidResponse} guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { throw NetworkError.invalidResponse }
do { do {
return try decoder.decode(Schedule.self, from: data) return try decoder.decode(Schedule.self, from: data)
@ -46,9 +46,9 @@ final class NetworkManager {
func getScheduleForOtherWeek(_ numOfWeek: Int, _ htmlNameOfGroup: String) async throws -> Schedule { func getScheduleForOtherWeek(_ numOfWeek: Int, _ htmlNameOfGroup: String) async throws -> Schedule {
let newUrlForWeek = makeUrlForWeek(numOfWeek, htmlNameOfGroup) let newUrlForWeek = makeUrlForWeek(numOfWeek, htmlNameOfGroup)
print(newUrlForWeek) print(newUrlForWeek)
guard let url = URL(string: newUrlForWeek) else {throw NetworkError.invalidUrl} guard let url = URL(string: newUrlForWeek) else { throw NetworkError.invalidUrl }
let (data, response) = try await URLSession.shared.data(from: url) let (data, response) = try await URLSession.shared.data(from: url)
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {throw NetworkError.invalidResponse} guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { throw NetworkError.invalidResponse }
do { do {
return try decoder.decode(Schedule.self, from: data) return try decoder.decode(Schedule.self, from: data)
@ -57,4 +57,18 @@ final class NetworkManager {
throw NetworkError.invalidData throw NetworkError.invalidData
} }
} }
func getGroups(group: String) async throws -> Welcome {
let newUrlForGroups = makeUrlForGroup(group)
guard let url = URL(string: newUrlForGroups) else { throw NetworkError.invalidUrl }
let (data, response) = try await URLSession.shared.data(from: url)
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { throw NetworkError.invalidResponse }
do {
return try decoder.decode(Welcome.self, from: data)
}
catch {
throw NetworkError.invalidData
}
}
} }

View File

@ -10,6 +10,7 @@ import Foundation
@MainActor @MainActor
final class ScheduleViewModel: ObservableObject { final class ScheduleViewModel: ObservableObject {
//MARK: Properties //MARK: Properties
//Schedule
@Published var weekSchedule: Table = Table( @Published var weekSchedule: Table = Table(
type: "", type: "",
name: "", name: "",
@ -30,6 +31,12 @@ final class ScheduleViewModel: ObservableObject {
@Published var group: String = "" @Published var group: String = ""
@Published var isNewGroup: Bool = false @Published var isNewGroup: Bool = false
//Groups
@Published var groups: [Choice] = []
//VPK
@Published var vpk: [[String]] = []
//MARK: Methods //MARK: Methods
func fetchWeekSchedule(group: String = "default", isOtherWeek: Bool = false) { func fetchWeekSchedule(group: String = "default", isOtherWeek: Bool = false) {
isLoading = true isLoading = true
@ -42,10 +49,11 @@ final class ScheduleViewModel: ObservableObject {
} }
// В else мы заходим в том случае, если не знаем номер недели, которую нужно отобразить и номер группы(в html формате) // В else мы заходим в том случае, если не знаем номер недели, которую нужно отобразить и номер группы(в html формате)
else { else {
print("Отладка 1")
schedule = try await NetworkManager.shared.getSchedule(group) schedule = try await NetworkManager.shared.getSchedule(group)
if (!self.isFirstStartOffApp) { print("Отладка 2")
self.isNewGroup = true self.group = group
} self.isNewGroup = true
self.selectedDay = .init() self.selectedDay = .init()
} }
self.weekSchedule = schedule.table self.weekSchedule = schedule.table
@ -56,7 +64,7 @@ final class ScheduleViewModel: ObservableObject {
self.isShowingAlertForIncorrectGroup = false self.isShowingAlertForIncorrectGroup = false
self.isLoading = false self.isLoading = false
self.errorInNetwork = .noError self.errorInNetwork = .noError
print("Отладка 4")
} }
catch { catch {
if let error = error as? NetworkError { if let error = error as? NetworkError {
@ -67,10 +75,33 @@ final class ScheduleViewModel: ObservableObject {
errorInNetwork = .invalidData errorInNetwork = .invalidData
self.isShowingAlertForIncorrectGroup = true self.isShowingAlertForIncorrectGroup = true
default: default:
print(2) print("Неизвестная ошибка: \(error)")
} }
isLoading = false isLoading = false
print(error) print("Есть ошибка: \(error)")
}
}
}
}
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)")
} }
} }
} }