From f6e1d979fd8520f834a8f7b71331842c4aba6b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Mu=CC=88ller?= Date: Wed, 7 Apr 2021 21:15:22 +0200 Subject: [PATCH] :lipstick: Updating events list view --- DHBW-Service.xcodeproj/project.pbxproj | 12 +- DHBW-Service/Utility/DateExtension.swift | 18 +++ DHBW-Service/Utility/RaPlaFetcher.swift | 6 +- DHBW-Service/Views/Tabs/LecturePlanList.swift | 149 ++++++++++++++---- 4 files changed, 146 insertions(+), 39 deletions(-) create mode 100644 DHBW-Service/Utility/DateExtension.swift diff --git a/DHBW-Service.xcodeproj/project.pbxproj b/DHBW-Service.xcodeproj/project.pbxproj index c39a93f..91e1546 100644 --- a/DHBW-Service.xcodeproj/project.pbxproj +++ b/DHBW-Service.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ CD9FABA3258EC60600D6D0C5 /* DHBW_ServiceUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD9FABA2258EC60600D6D0C5 /* DHBW_ServiceUITests.swift */; }; CDA1CBAE25D4591000DB2AE5 /* User+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDA1CBA825D4591000DB2AE5 /* User+CoreDataProperties.swift */; }; CDA1CBB025D4591000DB2AE5 /* Lecturer+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDA1CBAA25D4591000DB2AE5 /* Lecturer+CoreDataProperties.swift */; }; + CDB1E947261DDA0200EDE9EB /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDB1E946261DDA0200EDE9EB /* DateExtension.swift */; }; CDCD721A25912E1200FBF2F5 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDCD721925912E1200FBF2F5 /* HomeView.swift */; }; CDCD72242591316500FBF2F5 /* LocalSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDCD72232591316500FBF2F5 /* LocalSettings.swift */; }; CDCD7230259135C500FBF2F5 /* FirstOpeningSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDCD722F259135C500FBF2F5 /* FirstOpeningSettings.swift */; }; @@ -70,7 +71,6 @@ CD730A34259A860E00E0BB69 /* SettingsAcknowledgements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAcknowledgements.swift; sourceTree = ""; }; CD8555BD25C47AE500C4ACD6 /* RaPlaFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RaPlaFetcher.swift; sourceTree = ""; }; CD8555C225C47B5300C4ACD6 /* ApiService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiService.swift; sourceTree = ""; }; - CD9E3F0A25DC3C9A00C77D10 /* DHBW-Service.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "DHBW-Service.entitlements"; sourceTree = ""; }; CD9E3F0E25DC466100C77D10 /* SettingsPushNotifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPushNotifications.swift; sourceTree = ""; }; CD9FAB7D258EC60200D6D0C5 /* DHBW-Service.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DHBW-Service.app"; sourceTree = BUILT_PRODUCTS_DIR; }; CD9FAB80258EC60200D6D0C5 /* DHBW_ServiceApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DHBW_ServiceApp.swift; sourceTree = ""; }; @@ -88,6 +88,7 @@ CD9FABA4258EC60600D6D0C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; CDA1CBA825D4591000DB2AE5 /* User+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "User+CoreDataProperties.swift"; path = "DHBW-Service/CoreData/Models/User+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; }; CDA1CBAA25D4591000DB2AE5 /* Lecturer+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Lecturer+CoreDataProperties.swift"; path = "DHBW-Service/CoreData/Models/Lecturer+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; }; + CDB1E946261DDA0200EDE9EB /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = ""; }; CDCD721925912E1200FBF2F5 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; CDCD72232591316500FBF2F5 /* LocalSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalSettings.swift; sourceTree = ""; }; CDCD722F259135C500FBF2F5 /* FirstOpeningSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstOpeningSettings.swift; sourceTree = ""; }; @@ -210,7 +211,6 @@ CD9FAB7F258EC60200D6D0C5 /* DHBW-Service */ = { isa = PBXGroup; children = ( - CD9E3F0A25DC3C9A00C77D10 /* DHBW-Service.entitlements */, CDCD720F25912D3C00FBF2F5 /* App */, CDCD721025912D4900FBF2F5 /* Views */, CDDCF4792591FE410027CDC5 /* Utility */, @@ -321,6 +321,7 @@ CDDCF47A2591FE550027CDC5 /* UtilityFunctions.swift */, CD8555BD25C47AE500C4ACD6 /* RaPlaFetcher.swift */, CD8555C225C47B5300C4ACD6 /* ApiService.swift */, + CDB1E946261DDA0200EDE9EB /* DateExtension.swift */, ); path = Utility; sourceTree = ""; @@ -487,6 +488,7 @@ CD8555BE25C47AE500C4ACD6 /* RaPlaFetcher.swift in Sources */, CDEA70C025C85999001CFE28 /* LecturePlanItem.swift in Sources */, CD8555C325C47B5300C4ACD6 /* ApiService.swift in Sources */, + CDB1E947261DDA0200EDE9EB /* DateExtension.swift in Sources */, CD9FAB8D258EC60600D6D0C5 /* DHBW_Service.xcdatamodeld in Sources */, CDCD721A25912E1200FBF2F5 /* HomeView.swift in Sources */, CDD39B4B259A64150078D05F /* SettingsMain.swift in Sources */, @@ -683,9 +685,8 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = "DHBW-Service/DHBW-Service.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1.0.1; + CURRENT_PROJECT_VERSION = 1.0.2; DEVELOPMENT_ASSET_PATHS = "\"DHBW-Service/Preview Content\""; DEVELOPMENT_TEAM = HS7KNT4MZ2; ENABLE_PREVIEWS = YES; @@ -707,9 +708,8 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = "DHBW-Service/DHBW-Service.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1.0.1; + CURRENT_PROJECT_VERSION = 1.0.2; DEVELOPMENT_ASSET_PATHS = "\"DHBW-Service/Preview Content\""; DEVELOPMENT_TEAM = HS7KNT4MZ2; ENABLE_PREVIEWS = YES; diff --git a/DHBW-Service/Utility/DateExtension.swift b/DHBW-Service/Utility/DateExtension.swift new file mode 100644 index 0000000..f8ae4c2 --- /dev/null +++ b/DHBW-Service/Utility/DateExtension.swift @@ -0,0 +1,18 @@ +// +// DateExtension.swift +// DHBW-Service +// +// Created by Patrick Müller on 07.04.21. +// + +import Foundation + +extension Date { + func get(_ components: Calendar.Component..., calendar: Calendar = Calendar.current) -> DateComponents { + return calendar.dateComponents(Set(components), from: self) + } + + func get(_ component: Calendar.Component, calendar: Calendar = Calendar.current) -> Int { + return calendar.component(component, from: self) + } +} diff --git a/DHBW-Service/Utility/RaPlaFetcher.swift b/DHBW-Service/Utility/RaPlaFetcher.swift index e907d92..364cc5e 100644 --- a/DHBW-Service/Utility/RaPlaFetcher.swift +++ b/DHBW-Service/Utility/RaPlaFetcher.swift @@ -264,6 +264,8 @@ class RaPlaFetcher { print("Found unknown frequency: " + event.frequency) } + // (offsetAmount * iteration) because for the 1st event, we dont want to add an offset, and + // for every event after that we want to add e.g. 1 week, 2 weeks, 3 weeks etc. let startDate = Calendar.current.date(byAdding: offsetType, value: (offsetAmount * iteration), to: event.startDate)! let endDate = Calendar.current.date(byAdding: offsetType, value: (offsetAmount * iteration), to: event.endDate)! @@ -288,8 +290,6 @@ class RaPlaFetcher { } // Populate fields - // (offsetAmount * iteration) because for the 1st event, we dont want to add an offset, and - // for every event after that we want to add e.g. 1 week, 2 weeks, 3 weeks etc. evt.startDate = startDate evt.endDate = endDate evt.summary = event.summary @@ -298,6 +298,8 @@ class RaPlaFetcher { evt.category = event.category evt.uid = newUID for lecturer in event.lecturers { + // TODO: Delete all old lecturer objects + let lect = Lecturer(context: PersistenceController.shared.context) lect.name = lecturer.name lect.email = lecturer.email diff --git a/DHBW-Service/Views/Tabs/LecturePlanList.swift b/DHBW-Service/Views/Tabs/LecturePlanList.swift index 9109066..88ad61f 100644 --- a/DHBW-Service/Views/Tabs/LecturePlanList.swift +++ b/DHBW-Service/Views/Tabs/LecturePlanList.swift @@ -10,36 +10,26 @@ import CoreData struct LecturePlanList: View { @State private var events: [RaPlaEvent] = [] + @State private var daysWithEvents: [Date:[RaPlaEvent]] = [:] @State private var sortingAscending = true var body: some View { NavigationView() { - List { - ForEach(events, id: \.self) { event in - NavigationLink(destination: LecturePlanItem(event: event)){ - HStack { - Text(formatDate(date: event.startDate!)) - .foregroundColor(getEventForegroundColor(for: event)) - Text(event.summary!) - .foregroundColor(getEventForegroundColor(for: event)) - - Spacer() - - if(event.isHidden) { - Image(systemName: "eye.slash") - .foregroundColor(.red) - } else { - Image(systemName: "eye") - } - } + ScrollView(.vertical) { + ScrollViewReader { scrollView in +// Button("Jump to today") { +// withAnimation(){ +// scrollView.scrollTo(8, anchor: .center) +// } +// } +// .padding() + + ForEach(daysWithEvents.sorted(by: {$0.key < $1.key}), id: \.key) { key, value in + let dayBlock = DayWithEventsBlock(date: key, eventsList: value, parent: self) + dayBlock } - // When an event gets updated from child view, reload it here as this will not trigger the onAppear() function - .onReceive(event.objectWillChange, perform: { _ in - let sectionSortDescriptor = NSSortDescriptor(key: "startDate", ascending: true) - let sortDescriptors = [sectionSortDescriptor] - self.events = [] - self.events = RaPlaEvent.getSpecified(sortDescriptors: sortDescriptors) - }) + + } } .navigationBarTitle(Text("Lectures")) @@ -58,10 +48,8 @@ struct LecturePlanList: View { } .navigationViewStyle(StackNavigationViewStyle()) .onAppear{ - let sectionSortDescriptor = NSSortDescriptor(key: "startDate", ascending: true) - let sortDescriptors = [sectionSortDescriptor] - self.events = [] - self.events = RaPlaEvent.getSpecified(sortDescriptors: sortDescriptors) + self.getRaPlaEvents() + self.findNextDay() } } } @@ -83,13 +71,13 @@ struct LecturePlanList_Previews: PreviewProvider { } extension LecturePlanList { - private func formatDate(date: Date) -> String { + public func formatDate(date: Date) -> String { let formatter = DateFormatter() formatter.dateStyle = .short return formatter.string(from: date) } - private func getEventForegroundColor(for event: RaPlaEvent) -> Color { + public func getEventForegroundColor(for event: RaPlaEvent) -> Color { var textColor: Color = .primary if(event.category! == "Prüfung") { textColor = Color.red @@ -99,4 +87,103 @@ extension LecturePlanList { return textColor } + + public func findNextDay() { + // As this list is already sorted ascending, we can just return the first event with a start date that is in the future + let sortedEvents = self.events.sorted(by: { $0.startDate! < $1.startDate! }) + for event in sortedEvents { + if(event.startDate! > Date()) { + //self.focusedEvent = event + return + } + } + } + + public func getRaPlaEvents() { + let sectionSortDescriptor = NSSortDescriptor(key: "startDate", ascending: true) + let sortDescriptors = [sectionSortDescriptor] + + var calendar = Calendar.current + calendar.timeZone = NSTimeZone.local + let dateFrom = calendar.startOfDay(for: Date()) + let fromPredicate = NSPredicate(format: "startDate >= %@", dateFrom as NSDate) + + self.events = [] + self.daysWithEvents = [:] + + self.events = RaPlaEvent.getSpecified(sortDescriptors: sortDescriptors, searchPredicate: fromPredicate) + + // Also write events to daysWithEvents map + for event in self.events { + let components = event.startDate!.get(.day, .month, .year) + let day = String(components.day!); let month = String(components.month!); let year = String(components.year!) + + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd" + let dayOnly = dateFormatter.date(from: String(year + "-" + month + "-" + day))! + + var eventsList = daysWithEvents[dayOnly] + if(eventsList == nil) { + eventsList = [] + } + eventsList!.append(event) + self.daysWithEvents[dayOnly] = eventsList + } + } +} + +struct DayWithEventsBlock: View { + @State var date: Date + @State var eventsList: [RaPlaEvent] + @State var parent: LecturePlanList + + var body: some View { + VStack { + Text(String(date.get(.day)) + "." + String(date.get(.month)) + "." + String(date.get(.year))) + .font(.title) + .frame(maxWidth: .infinity, alignment: .leading) + + VStack { + if(!eventsList.isEmpty){ + ForEach(eventsList, id: \.self) { event in + NavigationLink(destination: LecturePlanItem(event: event)) { + HStack { + Text(parent.formatDate(date: event.startDate!)) + .foregroundColor(parent.getEventForegroundColor(for: event)) + Text(event.summary!) + .foregroundColor(parent.getEventForegroundColor(for: event)) + + Spacer() + + if(event.isHidden) { + Image(systemName: "eye.slash") + .foregroundColor(.red) + } else { + Image(systemName: "eye") + } + } + .padding() + .background( + RoundedRectangle(cornerRadius: 10) + .fill(Color(#colorLiteral(red: 0.2549019754, green: 0.2745098174, blue: 0.3019607961, alpha: 1))) + ) + } + // When an event gets updated from child view, reload it here as this will not trigger the onAppear() function + .onReceive(event.objectWillChange, perform: { _ in + print("receiving event") + parent.getRaPlaEvents() + }) + } + } else { + Text("noLectures".localized(tableName: "HomeView")) + .frame(maxWidth: .infinity, alignment: .leading) + } + } + } + .padding() + .background( + RoundedRectangle(cornerRadius: 10) + .fill(Color.gray) + ) + } }