From 4fdec23e527ecb8b8ed34606bff708d128319f5e Mon Sep 17 00:00:00 2001 From: Vladimir Dubovik Date: Mon, 18 Nov 2024 19:19:52 +0300 Subject: [PATCH] q --- Schedule ICTIS/Helpers/OffsetKey.swift | 15 +++++ Schedule ICTIS/{ => Model}/Model.swift | 4 +- Schedule ICTIS/Network/NetworkError.swift | 14 +++++ Schedule ICTIS/Network/NetworkManager.swift | 35 +++++++++++ Schedule ICTIS/ScheduleView.swift | 67 +++++++++++++++++++-- Schedule ICTIS/ViewModel/ViewModel.swift | 60 ++++++++++++++++++ 6 files changed, 189 insertions(+), 6 deletions(-) create mode 100644 Schedule ICTIS/Helpers/OffsetKey.swift rename Schedule ICTIS/{ => Model}/Model.swift (84%) create mode 100644 Schedule ICTIS/Network/NetworkError.swift create mode 100644 Schedule ICTIS/Network/NetworkManager.swift create mode 100644 Schedule ICTIS/ViewModel/ViewModel.swift diff --git a/Schedule ICTIS/Helpers/OffsetKey.swift b/Schedule ICTIS/Helpers/OffsetKey.swift new file mode 100644 index 0000000..d65b82a --- /dev/null +++ b/Schedule ICTIS/Helpers/OffsetKey.swift @@ -0,0 +1,15 @@ +// +// OffsetKey.swift +// Schedule ICTIS +// +// Created by G412 on 18.11.2024. +// + +import SwiftUI + +struct OffsetKey: PreferenceKey { + static var defaultValue: CGFloat = 0 + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { + value = nextValue() + } +} diff --git a/Schedule ICTIS/Model.swift b/Schedule ICTIS/Model/Model.swift similarity index 84% rename from Schedule ICTIS/Model.swift rename to Schedule ICTIS/Model/Model.swift index a785ea7..435982f 100644 --- a/Schedule ICTIS/Model.swift +++ b/Schedule ICTIS/Model/Model.swift @@ -8,13 +8,13 @@ import Foundation // MARK: - Welcome -struct Schedule: Codable { +struct Schedule: Decodable { let table: Table let weeks: [Int] } // MARK: - Table -struct Table: Codable { +struct Table: Decodable { let type, name: String let week: Int let group: String diff --git a/Schedule ICTIS/Network/NetworkError.swift b/Schedule ICTIS/Network/NetworkError.swift new file mode 100644 index 0000000..c20ee5d --- /dev/null +++ b/Schedule ICTIS/Network/NetworkError.swift @@ -0,0 +1,14 @@ +// +// NetworkError.swift +// NewsApp +// +// Created by Mironov Egor on 18.11.2024. +// + +import Foundation + +enum NetworkError: String, Error { + case invalidUrl = "Invalid URL" + case invalidResponse = "Invalid response form the server" + case invalidData = "Data received from the server is invalid" +} diff --git a/Schedule ICTIS/Network/NetworkManager.swift b/Schedule ICTIS/Network/NetworkManager.swift new file mode 100644 index 0000000..5ed4319 --- /dev/null +++ b/Schedule ICTIS/Network/NetworkManager.swift @@ -0,0 +1,35 @@ +// +// NetworkManager.swift +// NewsApp +// +// Created by Egor Mironov on 18.11.2024. +// + +import Foundation + +final class NetworkManager { + + //MARK: Properties + static let shared = NetworkManager() + private let decoder = JSONDecoder() + private let urlString = "https://webictis.sfedu.ru/schedule-api/?query=%D0%BA%D1%82%D0%B1%D0%BE2-6" + + //MARK: Initializer + private init() { + decoder.dateDecodingStrategy = .iso8601 + } + + //MARK: Methods + func getSchedule() async throws -> Schedule { + guard let url = URL(string: urlString) 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(Schedule.self, from: data) + } + catch { + throw NetworkError.invalidData + } + } +} diff --git a/Schedule ICTIS/ScheduleView.swift b/Schedule ICTIS/ScheduleView.swift index ebcbaa8..f2ed0b4 100644 --- a/Schedule ICTIS/ScheduleView.swift +++ b/Schedule ICTIS/ScheduleView.swift @@ -12,10 +12,23 @@ struct ScheduleView: View { @State private var currentDate: Date = .init() @State private var weekSlider: [[Date.WeekDay]] = [] @State private var currentWeekIndex: Int = 1 + @State private var createWeek: Bool = false + @StateObject var vm = ViewModel() + var body: some View { VStack { SearchBarView(text: $searchText) HeaderView() + ScrollView(.vertical) { + VStack { + ForEach(vm.weekSchedule, id: \.week) { day in + HStack { + Text(convertTimeString(day.table[1][1])[0]) + Text(convertTimeString(day.table[1][1])[9]) + } + } + } + } Spacer() } .background(.secondary.opacity(0.1)) @@ -34,7 +47,6 @@ struct ScheduleView: View { } } }) - } @ViewBuilder @@ -45,7 +57,6 @@ struct ScheduleView: View { Text(currentDate.format("EEEE")) .font(.system(size: 40, weight: .semibold)) .foregroundStyle(.black) -// .background(Color.green) HStack (spacing: 5) { Text(currentDate.format("dd")) .font(.system(size: 20, weight: .bold)) @@ -54,11 +65,9 @@ struct ScheduleView: View { .font(.system(size: 20, weight: .bold)) .foregroundStyle(Color("grayForDate")) } -// .background(.red) } .padding(.top, 8) .padding(.leading, 20) -// .background(Color.brown) Spacer() } .frame(maxWidth: .infinity) @@ -67,12 +76,20 @@ struct ScheduleView: View { ForEach(weekSlider.indices, id: \.self) { index in let week = weekSlider[index] WeekView(week) + .padding(.horizontal, 15) .tag(index) } } + .padding(.horizontal, -15) .tabViewStyle(.page(indexDisplayMode: .never)) .frame(height: 90) } + .onChange(of: currentWeekIndex, initial: false) { oldValue, newValue in + if newValue == 0 || newValue == (weekSlider.count - 1) { + createWeek = true + } + vm.updateSelectedIndex(newValue) + } } @ViewBuilder @@ -116,9 +133,51 @@ struct ScheduleView: View { .cornerRadius(15) .onTapGesture { currentDate = day.date + vm.updateSelectedDayIndex(currentDate) } } } + .background { + GeometryReader { + let minX = $0.frame(in: .global).minX + + Color.clear + .preference(key: OffsetKey.self, value: minX) + .onPreferenceChange(OffsetKey.self) { value in + if value.rounded() == 15 && createWeek { + paginateWeek() + createWeek = false + } + } + } + } + } + + func paginateWeek() { + if weekSlider.indices.contains(currentWeekIndex) { + if let firstDate = weekSlider[currentWeekIndex].first?.date, + currentWeekIndex == 0 { + weekSlider.insert(firstDate.createPrevioustWeek(), at: 0) + weekSlider.removeLast() + currentWeekIndex = 1 + } + + if let lastDate = weekSlider[currentWeekIndex].last?.date, + currentWeekIndex == (weekSlider.count - 1) { + weekSlider.append(lastDate.createNextWeek()) + weekSlider.removeFirst() + currentWeekIndex = weekSlider.count - 2 + } + } + } + + func convertTimeString(_ input: String) -> [String] { + let parts = input.split(separator: "-") + if let firstPart = parts.first, let lastPart = parts.last { + return [String(firstPart), String(lastPart)] + } else { + return [] + } } } diff --git a/Schedule ICTIS/ViewModel/ViewModel.swift b/Schedule ICTIS/ViewModel/ViewModel.swift new file mode 100644 index 0000000..395665b --- /dev/null +++ b/Schedule ICTIS/ViewModel/ViewModel.swift @@ -0,0 +1,60 @@ +// +// ViewModel.swift +// NewsApp +// +// Created by Mironov Egor on 18.11.2024. +// + +import Foundation + +@MainActor +final class ViewModel: ObservableObject { + //MARK: Properties + @Published var weekSchedule: [Table] = [] + @Published var selectedDay: Date = Date() + @Published var selectedIndex: Int = 0 + + init() { + fetchWeekSchedule() + } + + //MARK: Methods + func fetchWeekSchedule(){ + Task { + do { + let schedule = try await NetworkManager.shared.getSchedule() + weekSchedule = [schedule.table] + } + catch { + if let error = error as? NetworkError { + print(error) + } + } + } + } + + func updateSelectedDayIndex(_ date: Date) { + switch date.format("E") { + case "Пн": + selectedIndex = 1 + case "Вт": + selectedIndex = 2 + case "Ср": + selectedIndex = 3 + case "Чт": + selectedIndex = 4 + case "Пт": + selectedIndex = 5 + case "Сб": + selectedIndex = 6 + default: + selectedIndex = 7 + } + print(selectedIndex) + } + + func updateSelectedIndex(_ index: Int) { + selectedIndex = index + print(selectedIndex) + } +}