This commit is contained in:
Vladimir Dubovik 2024-11-18 19:19:52 +03:00
parent 3670a0b01f
commit 4fdec23e52
6 changed files with 189 additions and 6 deletions

View File

@ -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()
}
}

View File

@ -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

View File

@ -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"
}

View File

@ -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
}
}
}

View File

@ -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 []
}
}
}

View File

@ -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)
}
}