Commit
This commit is contained in:
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<array/>
|
||||||
|
</plist>
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
|
"filename" : "ICTIS_logo.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"platform" : "ios",
|
"platform" : "ios",
|
||||||
"size" : "1024x1024"
|
"size" : "1024x1024"
|
||||||
|
BIN
Schedule-ICTIS/Assets.xcassets/AppIcon.appiconset/ICTIS_logo.png
Normal file
BIN
Schedule-ICTIS/Assets.xcassets/AppIcon.appiconset/ICTIS_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
21
Schedule-ICTIS/Assets.xcassets/ICTIS_logo.imageset/Contents.json
vendored
Normal file
21
Schedule-ICTIS/Assets.xcassets/ICTIS_logo.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "ICTIS_logo.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
Schedule-ICTIS/Assets.xcassets/ICTIS_logo.imageset/ICTIS_logo.png
vendored
Normal file
BIN
Schedule-ICTIS/Assets.xcassets/ICTIS_logo.imageset/ICTIS_logo.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
21
Schedule-ICTIS/Assets.xcassets/arrowRight.imageset/Contents.json
vendored
Normal file
21
Schedule-ICTIS/Assets.xcassets/arrowRight.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "arrowRight.svg",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
3
Schedule-ICTIS/Assets.xcassets/arrowRight.imageset/arrowRight.svg
vendored
Normal file
3
Schedule-ICTIS/Assets.xcassets/arrowRight.imageset/arrowRight.svg
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M9 6L15 12L9 18" stroke="#8B8B8B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 212 B |
21
Schedule-ICTIS/Assets.xcassets/arrowdown.imageset/Contents.json
vendored
Normal file
21
Schedule-ICTIS/Assets.xcassets/arrowdown.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "arrowdown.svg",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
3
Schedule-ICTIS/Assets.xcassets/arrowdown.imageset/arrowdown.svg
vendored
Normal file
3
Schedule-ICTIS/Assets.xcassets/arrowdown.imageset/arrowdown.svg
vendored
Normal 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 |
21
Schedule-ICTIS/Assets.xcassets/arrowup.imageset/Contents.json
vendored
Normal file
21
Schedule-ICTIS/Assets.xcassets/arrowup.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "arrowup.svg",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
3
Schedule-ICTIS/Assets.xcassets/arrowup.imageset/arrowup.svg
vendored
Normal file
3
Schedule-ICTIS/Assets.xcassets/arrowup.imageset/arrowup.svg
vendored
Normal 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 |
21
Schedule-ICTIS/Assets.xcassets/upDownArrows.imageset/Contents.json
vendored
Normal file
21
Schedule-ICTIS/Assets.xcassets/upDownArrows.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "upDownArrows.svg",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
4
Schedule-ICTIS/Assets.xcassets/upDownArrows.imageset/upDownArrows.svg
vendored
Normal file
4
Schedule-ICTIS/Assets.xcassets/upDownArrows.imageset/upDownArrows.svg
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="14" height="18" viewBox="0 0 14 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M1 7L7 1L13 7" stroke="#878787" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M13 11L7 17L1 11" stroke="#878787" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 320 B |
@ -34,6 +34,18 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
TabBarView(selectedTab: $selectedTab)
|
TabBarView(selectedTab: $selectedTab)
|
||||||
}
|
}
|
||||||
|
.alert(isPresented: $vm.isShowingAlertForIncorrectSingleGroup, error: vm.errorInNetworkForSingleGroup) { error in
|
||||||
|
Button("ОК") {
|
||||||
|
print("This alert")
|
||||||
|
vm.isShowingAlertForIncorrectSingleGroup = false
|
||||||
|
vm.errorInNetworkForSingleGroup = nil
|
||||||
|
}
|
||||||
|
} message: { error in
|
||||||
|
Text(error.failureReason)
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
vm.fillFilteringGroups()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<string>Montserrat-BlackItalic.ttf</string>
|
<string>Montserrat-BlackItalic.ttf</string>
|
||||||
<string>Montserrat-Bold.ttf</string>
|
<string>Montserrat-Bold.ttf</string>
|
||||||
<string>Montserrat-BoldItalic.ttf</string>
|
<string>Montserrat-BoldItalic.ttf</string>
|
||||||
<string>Monsterrat-ExtraBold.ttf</string>
|
<string>Montserrat-ExtraBold.ttf</string>
|
||||||
<string>Montserrat-ExtraBoldItalic.ttf</string>
|
<string>Montserrat-ExtraBoldItalic.ttf</string>
|
||||||
<string>Montserrat-ExtraLight.ttf</string>
|
<string>Montserrat-ExtraLight.ttf</string>
|
||||||
<string>Montserrat-ExtraLightItalic.ttf</string>
|
<string>Montserrat-ExtraLightItalic.ttf</string>
|
||||||
|
@ -9,43 +9,69 @@ import SwiftUI
|
|||||||
|
|
||||||
struct FilterGroupsView: View {
|
struct FilterGroupsView: View {
|
||||||
@ObservedObject var vm: ScheduleViewModel
|
@ObservedObject var vm: ScheduleViewModel
|
||||||
|
@ObservedObject var networkMonitor: NetworkMonitor
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
LazyHStack(spacing: 12) {
|
LazyHStack(spacing: 12) {
|
||||||
ForEach(vm.filteringGroups, id: \.self) { group in
|
// Кнопка добавления новой группы
|
||||||
VStack {
|
NavigationLink(destination: SelectingGroupView(vm: vm, networkMonitor: networkMonitor)) {
|
||||||
Text(group)
|
ZStack {
|
||||||
|
Rectangle()
|
||||||
|
.frame(width: 28, height: 28)
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
.cornerRadius(20)
|
||||||
|
Image(systemName: "plus")
|
||||||
.foregroundColor(Color("customGray3"))
|
.foregroundColor(Color("customGray3"))
|
||||||
.font(.custom("Montserrat-Medium", fixedSize: 14))
|
.font(.system(size: 12))
|
||||||
.padding(.horizontal, 15)
|
|
||||||
.padding(.vertical, 7)
|
|
||||||
}
|
}
|
||||||
.background(Color.white)
|
}
|
||||||
.overlay (
|
|
||||||
Group {
|
if vm.filteringGroups.count == 2 {
|
||||||
if vm.showOnlyChoosenGroup == group {
|
ForEach(vm.filteringGroups.dropFirst(), id: \.self) { group in
|
||||||
RoundedRectangle(cornerRadius: 20)
|
GroupItem(group: group, isSelected: vm.showOnlyChoosenGroup == group) {
|
||||||
.stroke(Color("blueColor"), lineWidth: 3)
|
vm.showOnlyChoosenGroup = group
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else if vm.filteringGroups.count > 2 {
|
||||||
|
ForEach(vm.filteringGroups, id: \.self) { group in
|
||||||
|
GroupItem(group: group, isSelected: vm.showOnlyChoosenGroup == group) {
|
||||||
|
vm.showOnlyChoosenGroup = group
|
||||||
}
|
}
|
||||||
)
|
|
||||||
.cornerRadius(20)
|
|
||||||
.onTapGesture {
|
|
||||||
vm.showOnlyChoosenGroup = group
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
.frame(height: 40)
|
.frame(height: 40)
|
||||||
.onAppear {
|
|
||||||
vm.updateFilteringGroups()
|
|
||||||
}
|
|
||||||
.padding(.bottom, 4)
|
.padding(.bottom, 4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Вынесенный компонент для элемента группы
|
||||||
|
struct GroupItem: View {
|
||||||
|
let group: String
|
||||||
|
let isSelected: Bool
|
||||||
|
let action: () -> Void
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Text(group)
|
||||||
|
.foregroundColor(Color("customGray3"))
|
||||||
|
.font(.custom("Montserrat-Medium", fixedSize: 14))
|
||||||
|
.padding(.horizontal, 15)
|
||||||
|
.padding(.vertical, 7)
|
||||||
|
.background(Color.white)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 20)
|
||||||
|
.stroke(isSelected ? Color("blueColor") : Color.clear, lineWidth: 3)
|
||||||
|
)
|
||||||
|
.cornerRadius(20)
|
||||||
|
.onTapGesture(perform: action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
@Previewable @ObservedObject var vm = ScheduleViewModel()
|
@Previewable @ObservedObject var vm = ScheduleViewModel()
|
||||||
FilterGroupsView(vm: vm)
|
@Previewable @ObservedObject var networkMonitor = NetworkMonitor()
|
||||||
|
FilterGroupsView(vm: vm, networkMonitor: networkMonitor)
|
||||||
}
|
}
|
||||||
|
@ -14,32 +14,34 @@ struct MainView: View {
|
|||||||
@FocusState private var isFocusedSearchBar: Bool
|
@FocusState private var isFocusedSearchBar: Bool
|
||||||
@State private var isScrolling: Bool = false
|
@State private var isScrolling: Bool = false
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
NavigationStack {
|
||||||
SearchBarView(isFocused: _isFocusedSearchBar, vm: vm, isShowingMonthSlider: $isShowingMonthSlider)
|
VStack {
|
||||||
.onChange(of: isScrolling, initial: false) { oldValue, newValue in
|
SearchBarView(isFocused: _isFocusedSearchBar, vm: vm, isShowingMonthSlider: $isShowingMonthSlider)
|
||||||
if newValue && isScrolling {
|
.onChange(of: isScrolling, initial: false) { oldValue, newValue in
|
||||||
isFocusedSearchBar = false
|
if newValue && isScrolling {
|
||||||
|
isFocusedSearchBar = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
CurrentDateView()
|
||||||
|
FilterGroupsView(vm: vm, networkMonitor: networkMonitor)
|
||||||
|
if vm.isLoading {
|
||||||
|
LoadingScheduleView()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ScheduleView(vm: vm, networkMonitor: networkMonitor, isScrolling: $isScrolling)
|
||||||
}
|
}
|
||||||
CurrentDateView()
|
|
||||||
FilterGroupsView(vm: vm)
|
|
||||||
if vm.isLoading {
|
|
||||||
LoadingScheduleView()
|
|
||||||
}
|
}
|
||||||
else {
|
.alert(isPresented: $vm.isShowingAlertForIncorrectGroup, error: vm.errorInNetwork) { error in
|
||||||
ScheduleView(vm: vm, networkMonitor: networkMonitor, isScrolling: $isScrolling)
|
Button("ОК") {
|
||||||
|
print("This alert")
|
||||||
|
vm.isShowingAlertForIncorrectGroup = false
|
||||||
|
vm.errorInNetwork = nil
|
||||||
|
}
|
||||||
|
} message: { error in
|
||||||
|
Text(error.failureReason)
|
||||||
}
|
}
|
||||||
|
.background(Color("background"))
|
||||||
}
|
}
|
||||||
.alert(isPresented: $vm.isShowingAlertForIncorrectGroup, error: vm.errorInNetwork) { error in
|
|
||||||
Button("ОК") {
|
|
||||||
print("This alert")
|
|
||||||
vm.isShowingAlertForIncorrectGroup = false
|
|
||||||
vm.errorInNetwork = nil
|
|
||||||
}
|
|
||||||
} message: { error in
|
|
||||||
Text(error.failureReason)
|
|
||||||
}
|
|
||||||
.background(Color("background"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
|
@ -31,8 +31,9 @@ struct SearchBarView: View {
|
|||||||
if (!text.isEmpty) {
|
if (!text.isEmpty) {
|
||||||
vm.fetchWeekForSingleGroup(groupName: text)
|
vm.fetchWeekForSingleGroup(groupName: text)
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||||
guard vm.errorInNetwork == .noError else {
|
guard vm.errorInNetworkForSingleGroup == .noError else {
|
||||||
vm.isShowingAlertForIncorrectGroup = true
|
vm.isShowingAlertForIncorrectSingleGroup = true
|
||||||
|
self.text = ""
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
vm.removeFromSchedule(group: vm.searchingGroup)
|
vm.removeFromSchedule(group: vm.searchingGroup)
|
||||||
@ -40,7 +41,7 @@ struct SearchBarView: View {
|
|||||||
vm.searchingGroup = text
|
vm.searchingGroup = text
|
||||||
vm.nameToHtml[text] = ""
|
vm.nameToHtml[text] = ""
|
||||||
print("Ключи: \(vm.nameToHtml.keys)")
|
print("Ключи: \(vm.nameToHtml.keys)")
|
||||||
vm.updateFilteringGroups()
|
vm.addGroupToFilteringArray(group: text)
|
||||||
vm.fetchWeekSchedule()
|
vm.fetchWeekSchedule()
|
||||||
self.text = ""
|
self.text = ""
|
||||||
}
|
}
|
||||||
@ -77,12 +78,10 @@ struct SearchBarView: View {
|
|||||||
Rectangle()
|
Rectangle()
|
||||||
.frame(width: 40, height: 40)
|
.frame(width: 40, height: 40)
|
||||||
.foregroundStyle(Color("blueColor"))
|
.foregroundStyle(Color("blueColor"))
|
||||||
.cornerRadius(15)
|
.cornerRadius(10)
|
||||||
Image(systemName: "plus")
|
Image(systemName: "plus")
|
||||||
.resizable()
|
.foregroundColor(.white)
|
||||||
.foregroundStyle(.white)
|
.font(.system(size: 22))
|
||||||
.scaledToFit()
|
|
||||||
.frame(width: 16)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,8 +51,6 @@ extension JsonClassModel {
|
|||||||
|
|
||||||
let groupsToDelete = try context.fetch(fetchRequest)
|
let groupsToDelete = try context.fetch(fetchRequest)
|
||||||
|
|
||||||
print("Пары для удаления: \(groupsToDelete)")
|
|
||||||
|
|
||||||
for group in groupsToDelete {
|
for group in groupsToDelete {
|
||||||
do {
|
do {
|
||||||
try ClassProvider.shared.delete(group, in: context)
|
try ClassProvider.shared.delete(group, in: context)
|
||||||
|
@ -9,6 +9,7 @@ import SwiftUI
|
|||||||
import CoreData
|
import CoreData
|
||||||
|
|
||||||
struct FavGroupsView: View {
|
struct FavGroupsView: View {
|
||||||
|
@Environment(\.dismiss) private var dismiss
|
||||||
@ObservedObject var vm: ScheduleViewModel
|
@ObservedObject var vm: ScheduleViewModel
|
||||||
@ObservedObject var networkMonitor: NetworkMonitor
|
@ObservedObject var networkMonitor: NetworkMonitor
|
||||||
@FetchRequest(fetchRequest: FavouriteGroupModel.all()) private var favGroups // Список групп сохраненных в CoreData
|
@FetchRequest(fetchRequest: FavouriteGroupModel.all()) private var favGroups // Список групп сохраненных в CoreData
|
||||||
@ -39,28 +40,39 @@ struct FavGroupsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Spacer()
|
.navigationBarBackButtonHidden(true) // Скрываем стандартную кнопку "Назад"
|
||||||
|
.background(Color("background"))
|
||||||
HStack {
|
.toolbar {
|
||||||
Spacer()
|
ToolbarItem(placement: .topBarLeading) {
|
||||||
|
Button(action: {
|
||||||
|
dismiss()
|
||||||
|
}) {
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "chevron.left")
|
||||||
|
Text("Настройки")
|
||||||
|
}
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Кнопка в правой части
|
||||||
|
ToolbarItem(placement: .topBarTrailing) {
|
||||||
if favGroups.count < 10 {
|
if favGroups.count < 10 {
|
||||||
NavigationLink(destination: SelectingGroupView(vm: vm, networkMonitor: networkMonitor)) {
|
NavigationLink(destination: SelectingGroupView(vm: vm, networkMonitor: networkMonitor)) {
|
||||||
HStack {
|
ZStack {
|
||||||
|
Rectangle()
|
||||||
|
.frame(width: 40, height: 40)
|
||||||
|
.foregroundStyle(Color("blueColor"))
|
||||||
|
.cornerRadius(10)
|
||||||
Image(systemName: "plus")
|
Image(systemName: "plus")
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.font(.system(size: 22))
|
.font(.system(size: 18))
|
||||||
.padding(EdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 12))
|
|
||||||
}
|
}
|
||||||
.background(Color("blueColor"))
|
|
||||||
.cornerRadius(10)
|
|
||||||
.padding(.trailing, 20)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.bottom, 90)
|
|
||||||
}
|
}
|
||||||
.background(Color("background"))
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct FavVPKView: View {
|
struct FavVPKView: View {
|
||||||
|
@Environment(\.dismiss) private var dismiss
|
||||||
@ObservedObject var vm: ScheduleViewModel
|
@ObservedObject var vm: ScheduleViewModel
|
||||||
@ObservedObject var networkMonitor: NetworkMonitor
|
@ObservedObject var networkMonitor: NetworkMonitor
|
||||||
@FetchRequest(fetchRequest: FavouriteVpkModel.all()) private var favVpk // Список ВПК сохраненных в CoreData
|
@FetchRequest(fetchRequest: FavouriteVpkModel.all()) private var favVpk // Список ВПК сохраненных в CoreData
|
||||||
@ -38,28 +39,39 @@ struct FavVPKView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Spacer()
|
.navigationBarBackButtonHidden(true) // Скрываем стандартную кнопку "Назад"
|
||||||
|
.background(Color("background"))
|
||||||
HStack {
|
.toolbar {
|
||||||
Spacer()
|
ToolbarItem(placement: .topBarLeading) {
|
||||||
|
Button(action: {
|
||||||
|
dismiss()
|
||||||
|
}) {
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "chevron.left")
|
||||||
|
Text("Настройки")
|
||||||
|
}
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Кнопка в правой части
|
||||||
|
ToolbarItem(placement: .topBarTrailing) {
|
||||||
if favVpk.count < 5 {
|
if favVpk.count < 5 {
|
||||||
NavigationLink(destination: SelectingVPKView(vm: vm, networkMonitor: networkMonitor)) {
|
NavigationLink(destination: SelectingVPKView(vm: vm, networkMonitor: networkMonitor)) {
|
||||||
HStack {
|
ZStack {
|
||||||
|
Rectangle()
|
||||||
|
.frame(width: 40, height: 40)
|
||||||
|
.foregroundStyle(Color("blueColor"))
|
||||||
|
.cornerRadius(10)
|
||||||
Image(systemName: "plus")
|
Image(systemName: "plus")
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.font(.system(size: 22))
|
.font(.system(size: 18))
|
||||||
.padding(EdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 12))
|
|
||||||
}
|
}
|
||||||
.background(Color("blueColor"))
|
|
||||||
.cornerRadius(10)
|
|
||||||
.padding(.trailing, 20)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.bottom, 90)
|
|
||||||
}
|
}
|
||||||
.background(Color("background"))
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ struct ListOfGroupsView: View {
|
|||||||
try saveGroup(name: item.name)
|
try saveGroup(name: item.name)
|
||||||
saveScheduleForVpkToMemory(withName: item.name)
|
saveScheduleForVpkToMemory(withName: item.name)
|
||||||
vm.nameToHtml[item.name] = ""
|
vm.nameToHtml[item.name] = ""
|
||||||
vm.updateFilteringGroups()
|
vm.addGroupToFilteringArray(group: item.name)
|
||||||
vm.fetchWeekSchedule()
|
vm.fetchWeekSchedule()
|
||||||
dismiss()
|
dismiss()
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -45,30 +45,29 @@ struct SelectingGroupView: View {
|
|||||||
self.isFocused = false
|
self.isFocused = false
|
||||||
guard !text.isEmpty else { return }
|
guard !text.isEmpty else { return }
|
||||||
|
|
||||||
vm.fetchWeekForSingleGroup(groupName: text)
|
|
||||||
self.isLoading = true
|
self.isLoading = true
|
||||||
|
vm.fetchWeekForSingleGroup(groupName: text)
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||||
guard vm.errorInNetwork == .noError else {
|
if vm.errorInNetworkForSingleGroup != .noError {
|
||||||
vm.isShowingAlertForIncorrectGroup = true
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.errorInNetwork = nil
|
vm.errorInNetworkForSingleGroup = nil
|
||||||
let formattedText = transformStringToFormat(text)
|
let formattedText = transformStringToFormat(text)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try saveGroup(name: formattedText)
|
try saveGroup(name: formattedText)
|
||||||
saveScheduleForGroupToMemory(withName: formattedText)
|
saveScheduleForGroupToMemory(withName: formattedText)
|
||||||
vm.nameToHtml[formattedText] = ""
|
vm.nameToHtml[formattedText] = ""
|
||||||
vm.updateFilteringGroups()
|
vm.addGroupToFilteringArray(group: formattedText)
|
||||||
vm.fetchWeekSchedule()
|
vm.fetchWeekSchedule()
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
self.text = ""
|
self.text = ""
|
||||||
dismiss()
|
dismiss()
|
||||||
} catch {
|
} catch {
|
||||||
print("Ошибка сохранения: \(error.localizedDescription)")
|
print("Ошибка сохранения: \(error.localizedDescription)")
|
||||||
vm.isShowingAlertForIncorrectGroup = true
|
vm.isShowingAlertForIncorrectSingleGroup = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,10 +87,10 @@ struct SelectingGroupView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(height: 40)
|
.frame(height: 40)
|
||||||
.background(
|
.background (
|
||||||
RoundedRectangle(cornerRadius: 15)
|
RoundedRectangle(cornerRadius: 10)
|
||||||
.fill(.white)
|
.fill(.white)
|
||||||
)
|
)
|
||||||
Spacer()
|
Spacer()
|
||||||
if isLoading {
|
if isLoading {
|
||||||
LoadingView()
|
LoadingView()
|
||||||
@ -121,7 +120,7 @@ struct SelectingGroupView: View {
|
|||||||
try saveGroup(name: item.name)
|
try saveGroup(name: item.name)
|
||||||
saveScheduleForGroupToMemory(withName: item.name)
|
saveScheduleForGroupToMemory(withName: item.name)
|
||||||
vm.nameToHtml[item.name] = ""
|
vm.nameToHtml[item.name] = ""
|
||||||
vm.updateFilteringGroups()
|
vm.addGroupToFilteringArray(group: item.name)
|
||||||
vm.fetchWeekSchedule()
|
vm.fetchWeekSchedule()
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
self.text = ""
|
self.text = ""
|
||||||
@ -144,6 +143,21 @@ struct SelectingGroupView: View {
|
|||||||
.onAppear {
|
.onAppear {
|
||||||
serchGroupsVM.fetchGroups(group: "кт")
|
serchGroupsVM.fetchGroups(group: "кт")
|
||||||
}
|
}
|
||||||
|
.navigationBarBackButtonHidden(true) // Скрываем стандартную кнопку "Назад"
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .topBarLeading) {
|
||||||
|
Button(action: {
|
||||||
|
dismiss()
|
||||||
|
}) {
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "chevron.left")
|
||||||
|
Text("Назад")
|
||||||
|
}
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,30 +45,29 @@ struct SelectingVPKView: View {
|
|||||||
self.isFocused = false
|
self.isFocused = false
|
||||||
guard !text.isEmpty else { return }
|
guard !text.isEmpty else { return }
|
||||||
|
|
||||||
vm.fetchWeekForSingleGroup(groupName: text)
|
|
||||||
self.isLoading = true
|
self.isLoading = true
|
||||||
|
vm.fetchWeekForSingleGroup(groupName: text)
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||||
guard vm.errorInNetwork == .noError else {
|
guard vm.errorInNetworkForSingleGroup == .noError else {
|
||||||
vm.isShowingAlertForIncorrectGroup = true
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.errorInNetwork = nil
|
vm.errorInNetworkForSingleGroup = nil
|
||||||
let formattedText = transformStringToFormat(text)
|
let formattedText = transformStringToFormat(text)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try saveGroup(name: formattedText)
|
try saveGroup(name: formattedText)
|
||||||
saveScheduleForVpkToMemory(withName: formattedText)
|
saveScheduleForVpkToMemory(withName: formattedText)
|
||||||
vm.nameToHtml[formattedText] = ""
|
vm.nameToHtml[formattedText] = ""
|
||||||
vm.updateFilteringGroups()
|
vm.addGroupToFilteringArray(group: formattedText)
|
||||||
vm.fetchWeekSchedule()
|
vm.fetchWeekSchedule()
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
self.text = ""
|
self.text = ""
|
||||||
dismiss()
|
dismiss()
|
||||||
} catch {
|
} catch {
|
||||||
print("Ошибка сохранения: \(error.localizedDescription)")
|
print("Ошибка сохранения: \(error.localizedDescription)")
|
||||||
vm.isShowingAlertForIncorrectGroup = true
|
vm.isShowingAlertForIncorrectSingleGroup = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,6 +106,21 @@ struct SelectingVPKView: View {
|
|||||||
.onAppear {
|
.onAppear {
|
||||||
serchGroupsVM.fetchGroups(group: "ВПК")
|
serchGroupsVM.fetchGroups(group: "ВПК")
|
||||||
}
|
}
|
||||||
|
.navigationBarBackButtonHidden(true) // Скрываем стандартную кнопку "Назад"
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .topBarLeading) {
|
||||||
|
Button(action: {
|
||||||
|
dismiss()
|
||||||
|
}) {
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "chevron.left")
|
||||||
|
Text("Назад")
|
||||||
|
}
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ struct SettingsView: View {
|
|||||||
@State private var selectedTheme = "Светлая"
|
@State private var selectedTheme = "Светлая"
|
||||||
@State private var selectedLanguage = "Русский"
|
@State private var selectedLanguage = "Русский"
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView {
|
NavigationStack {
|
||||||
VStack {
|
VStack {
|
||||||
ScrollView (.vertical, showsIndicators: false) {
|
ScrollView (.vertical, showsIndicators: false) {
|
||||||
VStack (alignment: .leading) {
|
VStack (alignment: .leading) {
|
||||||
|
@ -21,7 +21,7 @@ enum NetworkError: String, Error, LocalizedError {
|
|||||||
case .invalidResponse:
|
case .invalidResponse:
|
||||||
"InvalidResponse"
|
"InvalidResponse"
|
||||||
case .invalidData:
|
case .invalidData:
|
||||||
"Проверьте номер группы"
|
"Проверьте имя группы"
|
||||||
case .timeout:
|
case .timeout:
|
||||||
"Ошибка сети"
|
"Ошибка сети"
|
||||||
case .noError:
|
case .noError:
|
@ -17,7 +17,7 @@ final class ScheduleViewModel: ObservableObject {
|
|||||||
@Published var classesGroups: [[ClassInfo]] = []
|
@Published var classesGroups: [[ClassInfo]] = []
|
||||||
@Published var searchingGroup = ""
|
@Published var searchingGroup = ""
|
||||||
@Published var filteringGroups: [String] = ["Все"]
|
@Published var filteringGroups: [String] = ["Все"]
|
||||||
@Published var showOnlyChoosenGroup: String = "Все"
|
@Published var showOnlyChoosenGroup: String = ""
|
||||||
|
|
||||||
//Schedule
|
//Schedule
|
||||||
@Published var weekScheduleGroup: Table = Table(
|
@Published var weekScheduleGroup: Table = Table(
|
||||||
@ -34,7 +34,9 @@ final class ScheduleViewModel: ObservableObject {
|
|||||||
|
|
||||||
@Published var isFirstStartOffApp = true
|
@Published var isFirstStartOffApp = true
|
||||||
@Published var isShowingAlertForIncorrectGroup: Bool = false
|
@Published var isShowingAlertForIncorrectGroup: Bool = false
|
||||||
|
@Published var isShowingAlertForIncorrectSingleGroup: Bool = false
|
||||||
@Published var errorInNetwork: NetworkError?
|
@Published var errorInNetwork: NetworkError?
|
||||||
|
@Published var errorInNetworkForSingleGroup: NetworkError?
|
||||||
@Published var isLoading: Bool = false
|
@Published var isLoading: Bool = false
|
||||||
@Published var isNewGroup: Bool = false
|
@Published var isNewGroup: Bool = false
|
||||||
|
|
||||||
@ -141,25 +143,25 @@ final class ScheduleViewModel: ObservableObject {
|
|||||||
|
|
||||||
// Обновляем данные
|
// Обновляем данные
|
||||||
self.classesForSingleGroup = singleSchedule
|
self.classesForSingleGroup = singleSchedule
|
||||||
self.isShowingAlertForIncorrectGroup = false
|
self.isShowingAlertForIncorrectSingleGroup = false
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
self.errorInNetwork = .noError
|
self.errorInNetworkForSingleGroup = .noError
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
if let urlError = error as? URLError, urlError.code == .timedOut {
|
if let urlError = error as? URLError, urlError.code == .timedOut {
|
||||||
errorInNetwork = .timeout
|
errorInNetworkForSingleGroup = .timeout
|
||||||
print("Ошибка: превышено время ожидания ответа от сервера")
|
print("Ошибка: превышено время ожидания ответа от сервера")
|
||||||
} else if let error = error as? NetworkError {
|
} else if let error = error as? NetworkError {
|
||||||
switch error {
|
switch error {
|
||||||
case .invalidResponse:
|
case .invalidResponse:
|
||||||
errorInNetwork = .invalidResponse
|
errorInNetworkForSingleGroup = .invalidResponse
|
||||||
case .invalidData:
|
case .invalidData:
|
||||||
errorInNetwork = .invalidData
|
errorInNetworkForSingleGroup = .invalidData
|
||||||
print("FetchSingle: InvalidData")
|
print("FetchSingle: InvalidData")
|
||||||
self.isShowingAlertForIncorrectGroup = true
|
|
||||||
default:
|
default:
|
||||||
print("Неизвестная ошибка: \(error)")
|
print("Неизвестная ошибка: \(error)")
|
||||||
}
|
}
|
||||||
|
self.isShowingAlertForIncorrectSingleGroup = true
|
||||||
print("Есть ошибка: \(error)")
|
print("Есть ошибка: \(error)")
|
||||||
}
|
}
|
||||||
isLoading = false
|
isLoading = false
|
||||||
@ -229,12 +231,34 @@ final class ScheduleViewModel: ObservableObject {
|
|||||||
classesGroups[i].remove(at: j)
|
classesGroups[i].remove(at: j)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.filteringGroups = self.filteringGroups.filter {$0 != group}
|
||||||
|
|
||||||
|
if group == self.showOnlyChoosenGroup || self.filteringGroups.count == 2 {
|
||||||
|
selectShowingGroup()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateFilteringGroups() {
|
func selectShowingGroup() {
|
||||||
self.filteringGroups = ["Все"]
|
if self.filteringGroups.count == 2 {
|
||||||
|
self.showOnlyChoosenGroup = self.filteringGroups[1]
|
||||||
|
} else if self.filteringGroups.count < 2 {
|
||||||
|
self.showOnlyChoosenGroup = ""
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.showOnlyChoosenGroup = "Все"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addGroupToFilteringArray(group: String) {
|
||||||
|
self.filteringGroups.append(group)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fillFilteringGroups() {
|
||||||
let keys = self.nameToHtml.keys
|
let keys = self.nameToHtml.keys
|
||||||
print(keys)
|
print(keys)
|
||||||
self.filteringGroups.append(contentsOf: keys)
|
self.filteringGroups.append(contentsOf: keys)
|
||||||
|
|
||||||
|
selectShowingGroup()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user