From be8bb15cddbb8deb46435d44929dfce018f32728 Mon Sep 17 00:00:00 2001 From: Adis Veletanlic Date: Fri, 27 Jan 2023 01:45:27 +0100 Subject: [PATCH] - Add expandable cards - Fix color storage Still need to fix random coloring in preview schedule and weird list animations in schedule view --- tumble-ios.xcodeproj/project.pbxproj | 40 ++++++++-- ...Exstensions.swift => FontExtensions.swift} | 5 +- .../Core/Extensions/ScheduleExtensions.swift | 64 ++++++++------- tumble-ios/Core/Utilities/Color.swift | 21 ----- .../ViewModels/HomePageView-ViewModel.swift | 1 - .../SchedulePageMainView-ViewModel.swift | 3 +- .../ViewModels/SearchView-ViewModel.swift | 77 +++++++++++-------- .../AppNavigation/AppNavigatorView.swift | 21 ++--- .../AppNavigation/DrawerButtonView.swift | 25 ++++++ .../AppNavigation/SearchButtonView.swift | 27 +++++++ .../HomePage/HomePageLinkOptionView.swift | 2 +- .../HomePage/HomePageLinkSectionView.swift | 4 +- .../HomePageQuickAccessOptionView.swift | 2 +- .../HomePageQuickAccessSectionView.swift | 4 +- .../Core/Views/HomePage/HomePageView.swift | 12 +-- .../Views/Schedule/List/CardBannerView.swift | 43 +++++++++++ .../Schedule/List/ScheduleCardView.swift | 66 +++++----------- .../Schedule/List/ScheduleListView.swift | 3 +- .../Preview/PreviewCardBannerView.swift | 35 +++++++++ .../Preview/SchedulePreviewCardView.swift | 50 +----------- .../Search/Preview/SchedulePreviewView.swift | 8 +- .../Services/API/Backend/APIClient.swift | 2 - .../API/Database/CourseColorStore.swift | 42 +++++----- .../Services/API/Database/ScheduleStore.swift | 2 +- 24 files changed, 315 insertions(+), 244 deletions(-) rename tumble-ios/Core/Extensions/{FontExstensions.swift => FontExtensions.swift} (76%) create mode 100644 tumble-ios/Core/Views/AppNavigation/DrawerButtonView.swift create mode 100644 tumble-ios/Core/Views/AppNavigation/SearchButtonView.swift create mode 100644 tumble-ios/Core/Views/Schedule/List/CardBannerView.swift create mode 100644 tumble-ios/Core/Views/Search/Preview/PreviewCardBannerView.swift diff --git a/tumble-ios.xcodeproj/project.pbxproj b/tumble-ios.xcodeproj/project.pbxproj index 0226e518..037c2ea3 100644 --- a/tumble-ios.xcodeproj/project.pbxproj +++ b/tumble-ios.xcodeproj/project.pbxproj @@ -29,7 +29,6 @@ D40E843D2981C8B200C2CF2E /* AccountPageView-ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D40E843C2981C8B200C2CF2E /* AccountPageView-ViewModel.swift */; }; D40E84422981E4E100C2CF2E /* ScheduleCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D40E84412981E4E100C2CF2E /* ScheduleCalendarView.swift */; }; D412F4542982D35F003154CC /* BottomBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D412F4532982D35F003154CC /* BottomBarItem.swift */; }; - D423321A29827C4E00F6CE9C /* ElegantCalendar.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D423321929827C4E00F6CE9C /* ElegantCalendar.xcassets */; }; D4239079293686D7005A95D7 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4239078293686D7005A95D7 /* Color.swift */; }; D43E680229820C9700124F72 /* ElegantCalendar in Frameworks */ = {isa = PBXBuildFile; productRef = D43E680129820C9700124F72 /* ElegantCalendar */; }; D45338F629280E8400198411 /* ScheduleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D45338F529280E8400198411 /* ScheduleExtensions.swift */; }; @@ -65,6 +64,8 @@ D476BD2A293A31FD0027CFE8 /* SchedulePreviewListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D476BD29293A31FD0027CFE8 /* SchedulePreviewListView.swift */; }; D476BD2C293A326C0027CFE8 /* SchedulePreviewCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D476BD2B293A326C0027CFE8 /* SchedulePreviewCardView.swift */; }; D476BD2E293A32BF0027CFE8 /* RoundedCornerShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = D476BD2D293A32BF0027CFE8 /* RoundedCornerShape.swift */; }; + D4861DEB29834E1900C4CA6F /* PreviewCardBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4861DEA29834E1900C4CA6F /* PreviewCardBannerView.swift */; }; + D4861DED29834EB900C4CA6F /* PreviewCardInformationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4861DEC29834EB900C4CA6F /* PreviewCardInformationView.swift */; }; D48F0D8829367A7F0039CD0F /* CourseColorStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48F0D8729367A7F0039CD0F /* CourseColorStore.swift */; }; D48F0D8A29367CAF0039CD0F /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48F0D8929367CAF0039CD0F /* Colors.swift */; }; D4967F832981F21000DE872E /* DateHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4967F822981F21000DE872E /* DateHolder.swift */; }; @@ -73,6 +74,11 @@ D4BAE614292466F700143717 /* SchoolModelData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4BAE613292466F700143717 /* SchoolModelData.swift */; }; D4BAE61929246AA200143717 /* schools.json in Resources */ = {isa = PBXBuildFile; fileRef = D4BAE61829246AA200143717 /* schools.json */; }; D4BAE61C29246AF800143717 /* SchoolRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4BAE61B29246AF800143717 /* SchoolRow.swift */; }; + D4CE2DAD2982D60000B81052 /* ElegantCalendar.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D4CE2DAC2982D60000B81052 /* ElegantCalendar.xcassets */; }; + D4CE2DB22982DD3500B81052 /* CardBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4CE2DB12982DD3500B81052 /* CardBannerView.swift */; }; + D4CE2DB42982DDFE00B81052 /* CardInformationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4CE2DB32982DDFE00B81052 /* CardInformationView.swift */; }; + D4CE2DBB2982E4D700B81052 /* DrawerButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4CE2DBA2982E4D700B81052 /* DrawerButtonView.swift */; }; + D4CE2DBD2982E55200B81052 /* SearchButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4CE2DBC2982E55200B81052 /* SearchButtonView.swift */; }; D4CF9B2229353FC800DDB5F0 /* HomePageLinkSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4CF9B2129353FC800DDB5F0 /* HomePageLinkSectionView.swift */; }; D4CF9B242935405200DDB5F0 /* HomePageLinkOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4CF9B232935405200DDB5F0 /* HomePageLinkOptionView.swift */; }; D4CF9B282935633800DDB5F0 /* SchedulePreviewError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4CF9B272935633800DDB5F0 /* SchedulePreviewError.swift */; }; @@ -92,7 +98,7 @@ D4D4DD1C2927CA780083BB7F /* roboto_light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D4D4DD102927CA780083BB7F /* roboto_light.ttf */; }; D4D4DD1D2927CA780083BB7F /* roboto_regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D4D4DD112927CA780083BB7F /* roboto_regular.ttf */; }; D4D4DD1E2927CA780083BB7F /* roboto_light_italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D4D4DD122927CA780083BB7F /* roboto_light_italic.ttf */; }; - D4D4DD202927CBDA0083BB7F /* FontExstensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4D4DD1F2927CBDA0083BB7F /* FontExstensions.swift */; }; + D4D4DD202927CBDA0083BB7F /* FontExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4D4DD1F2927CBDA0083BB7F /* FontExtensions.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -118,7 +124,6 @@ D40E843C2981C8B200C2CF2E /* AccountPageView-ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountPageView-ViewModel.swift"; sourceTree = ""; }; D40E84412981E4E100C2CF2E /* ScheduleCalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ScheduleCalendarView.swift; path = "tumble-ios/Core/Views/Schedule/Calendar/ScheduleCalendarView.swift"; sourceTree = SOURCE_ROOT; }; D412F4532982D35F003154CC /* BottomBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BottomBarItem.swift; path = ../../../../../../../.Trash/BottomBarItem.swift; sourceTree = ""; }; - D423321929827C4E00F6CE9C /* ElegantCalendar.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = ElegantCalendar.xcassets; path = "../../Library/Developer/Xcode/DerivedData/tumble-ios-fexkmcwqhbcmhwhafpnwukjkdktx/SourcePackages/checkouts/ElegantCalendar/ElegantCalendar.xcassets"; sourceTree = ""; }; D4239078293686D7005A95D7 /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; D45338F529280E8400198411 /* ScheduleExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleExtensions.swift; sourceTree = ""; }; D45338F8292825B200198411 /* ScheduleCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleCardView.swift; sourceTree = ""; }; @@ -151,6 +156,8 @@ D476BD29293A31FD0027CFE8 /* SchedulePreviewListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SchedulePreviewListView.swift; sourceTree = ""; }; D476BD2B293A326C0027CFE8 /* SchedulePreviewCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SchedulePreviewCardView.swift; sourceTree = ""; }; D476BD2D293A32BF0027CFE8 /* RoundedCornerShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedCornerShape.swift; sourceTree = ""; }; + D4861DEA29834E1900C4CA6F /* PreviewCardBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewCardBannerView.swift; sourceTree = ""; }; + D4861DEC29834EB900C4CA6F /* PreviewCardInformationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewCardInformationView.swift; sourceTree = ""; }; D48F0D8729367A7F0039CD0F /* CourseColorStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseColorStore.swift; sourceTree = ""; }; D48F0D8929367CAF0039CD0F /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = ""; }; D4967F822981F21000DE872E /* DateHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateHolder.swift; sourceTree = ""; }; @@ -159,6 +166,11 @@ D4BAE613292466F700143717 /* SchoolModelData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SchoolModelData.swift; sourceTree = ""; }; D4BAE61829246AA200143717 /* schools.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = schools.json; sourceTree = ""; }; D4BAE61B29246AF800143717 /* SchoolRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SchoolRow.swift; sourceTree = ""; }; + D4CE2DAC2982D60000B81052 /* ElegantCalendar.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = ElegantCalendar.xcassets; path = "../../Library/Developer/Xcode/DerivedData/tumble-ios-bmdzgpsrrlqprxfymxolonszzycj/SourcePackages/checkouts/ElegantCalendar/ElegantCalendar.xcassets"; sourceTree = ""; }; + D4CE2DB12982DD3500B81052 /* CardBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardBannerView.swift; sourceTree = ""; }; + D4CE2DB32982DDFE00B81052 /* CardInformationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardInformationView.swift; sourceTree = ""; }; + D4CE2DBA2982E4D700B81052 /* DrawerButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DrawerButtonView.swift; sourceTree = ""; }; + D4CE2DBC2982E55200B81052 /* SearchButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchButtonView.swift; sourceTree = ""; }; D4CF9B2129353FC800DDB5F0 /* HomePageLinkSectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePageLinkSectionView.swift; sourceTree = ""; }; D4CF9B232935405200DDB5F0 /* HomePageLinkOptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePageLinkOptionView.swift; sourceTree = ""; }; D4CF9B272935633800DDB5F0 /* SchedulePreviewError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SchedulePreviewError.swift; sourceTree = ""; }; @@ -178,7 +190,7 @@ D4D4DD102927CA780083BB7F /* roboto_light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = roboto_light.ttf; sourceTree = ""; }; D4D4DD112927CA780083BB7F /* roboto_regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = roboto_regular.ttf; sourceTree = ""; }; D4D4DD122927CA780083BB7F /* roboto_light_italic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = roboto_light_italic.ttf; sourceTree = ""; }; - D4D4DD1F2927CBDA0083BB7F /* FontExstensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontExstensions.swift; sourceTree = ""; }; + D4D4DD1F2927CBDA0083BB7F /* FontExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontExtensions.swift; sourceTree = ""; }; D4D4DD212927CFFA0083BB7F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ @@ -251,7 +263,7 @@ isa = PBXGroup; children = ( D40D80A529252165008CA68D /* ViewExtensions.swift */, - D4D4DD1F2927CBDA0083BB7F /* FontExstensions.swift */, + D4D4DD1F2927CBDA0083BB7F /* FontExtensions.swift */, D45338F529280E8400198411 /* ScheduleExtensions.swift */, D45338FE2929805100198411 /* StringExtensions.swift */, ); @@ -417,6 +429,8 @@ isa = PBXGroup; children = ( D4607633292AA83F00DE9590 /* AppNavigatorView.swift */, + D4CE2DBA2982E4D700B81052 /* DrawerButtonView.swift */, + D4CE2DBC2982E55200B81052 /* SearchButtonView.swift */, ); path = AppNavigation; sourceTree = ""; @@ -436,6 +450,8 @@ D4607652292AF5DA00DE9590 /* DayHeaderSectionView.swift */, D45338F8292825B200198411 /* ScheduleCardView.swift */, D460764B292AEAEC00DE9590 /* ScheduleListView.swift */, + D4CE2DB12982DD3500B81052 /* CardBannerView.swift */, + D4CE2DB32982DDFE00B81052 /* CardInformationView.swift */, ); path = List; sourceTree = ""; @@ -443,7 +459,7 @@ D472213C292454560063BDB7 = { isa = PBXGroup; children = ( - D423321929827C4E00F6CE9C /* ElegantCalendar.xcassets */, + D4CE2DAC2982D60000B81052 /* ElegantCalendar.xcassets */, D496CC5E298206710025C52A /* Packages */, D4722147292454560063BDB7 /* tumble-ios */, D4722146292454560063BDB7 /* Products */, @@ -531,6 +547,8 @@ D4CF9B272935633800DDB5F0 /* SchedulePreviewError.swift */, D476BD29293A31FD0027CFE8 /* SchedulePreviewListView.swift */, D476BD2B293A326C0027CFE8 /* SchedulePreviewCardView.swift */, + D4861DEA29834E1900C4CA6F /* PreviewCardBannerView.swift */, + D4861DEC29834EB900C4CA6F /* PreviewCardInformationView.swift */, ); path = Preview; sourceTree = ""; @@ -667,8 +685,8 @@ D4D4DD1C2927CA780083BB7F /* roboto_light.ttf in Resources */, D4D4DD1E2927CA780083BB7F /* roboto_light_italic.ttf in Resources */, D4D4DD142927CA780083BB7F /* roboto_thin.ttf in Resources */, - D423321A29827C4E00F6CE9C /* ElegantCalendar.xcassets in Resources */, D4D4DD152927CA780083BB7F /* roboto_black.ttf in Resources */, + D4CE2DAD2982D60000B81052 /* ElegantCalendar.xcassets in Resources */, D4D4DD182927CA780083BB7F /* roboto_bold_italic.ttf in Resources */, D4D4DD192927CA780083BB7F /* roboto_medium.ttf in Resources */, D4D4DD132927CA780083BB7F /* roboto_bold.ttf in Resources */, @@ -694,6 +712,7 @@ D4BAE61C29246AF800143717 /* SchoolRow.swift in Sources */, D40D808929247123008CA68D /* RootView.swift in Sources */, D48F0D8829367A7F0039CD0F /* CourseColorStore.swift in Sources */, + D4861DED29834EB900C4CA6F /* PreviewCardInformationView.swift in Sources */, D40D80D22926A0A6008CA68D /* DrawerView.swift in Sources */, D40CC161292AFB9F00664776 /* ScheduleMainPageView.swift in Sources */, D412F4542982D35F003154CC /* BottomBarItem.swift in Sources */, @@ -702,6 +721,7 @@ D40D80CE29265D49008CA68D /* SearchBar.swift in Sources */, D4CF9B282935633800DDB5F0 /* SchedulePreviewError.swift in Sources */, D40D80B429252A5A008CA68D /* UserDefaultsClient.swift in Sources */, + D4861DEB29834E1900C4CA6F /* PreviewCardBannerView.swift in Sources */, D4722149292454560063BDB7 /* tumble_iosApp.swift in Sources */, D4967F8F2981FA7600DE872E /* Month.swift in Sources */, D4722160292455710063BDB7 /* API.swift in Sources */, @@ -720,7 +740,9 @@ D40E843B2981C85400C2CF2E /* AccountPageView.swift in Sources */, D40D809B29251FD7008CA68D /* ToastStyle.swift in Sources */, D40D80CA29265B98008CA68D /* SearchParentView.swift in Sources */, + D4CE2DB42982DDFE00B81052 /* CardInformationView.swift in Sources */, D40D80BE29258CAF008CA68D /* BottomBarView.swift in Sources */, + D4CE2DBD2982E55200B81052 /* SearchButtonView.swift in Sources */, D4607645292AC41D00DE9590 /* SearchResultsView.swift in Sources */, D45338F9292825B200198411 /* ScheduleCardView.swift in Sources */, D476BD2A293A31FD0027CFE8 /* SchedulePreviewListView.swift in Sources */, @@ -729,6 +751,7 @@ D4607632292AA39400DE9590 /* Types.swift in Sources */, D4607653292AF5DA00DE9590 /* DayHeaderSectionView.swift in Sources */, D40D80A629252165008CA68D /* ViewExtensions.swift in Sources */, + D4CE2DBB2982E4D700B81052 /* DrawerButtonView.swift in Sources */, D476BD2E293A32BF0027CFE8 /* RoundedCornerShape.swift in Sources */, D45338FD29297C8B00198411 /* DayUIModel.swift in Sources */, D460764A292AE77E00DE9590 /* BubbleShape.swift in Sources */, @@ -739,6 +762,7 @@ D4BAE614292466F700143717 /* SchoolModelData.swift in Sources */, D45338FF2929805100198411 /* StringExtensions.swift in Sources */, D472215F292455710063BDB7 /* APITypes.swift in Sources */, + D4CE2DB22982DD3500B81052 /* CardBannerView.swift in Sources */, D4D4DD052927C88E0083BB7F /* BackButton.swift in Sources */, D476BD2C293A326C0027CFE8 /* SchedulePreviewCardView.swift in Sources */, D40E843D2981C8B200C2CF2E /* AccountPageView-ViewModel.swift in Sources */, @@ -747,7 +771,7 @@ D40D809D29251FF6008CA68D /* Toast.swift in Sources */, D4CF9B2C293572BE00DDB5F0 /* InfoView.swift in Sources */, D40CC163292AFE7200664776 /* SchedulePageMainView-ViewModel.swift in Sources */, - D4D4DD202927CBDA0083BB7F /* FontExstensions.swift in Sources */, + D4D4DD202927CBDA0083BB7F /* FontExtensions.swift in Sources */, D40D80CC29265C36008CA68D /* SearchView-ViewModel.swift in Sources */, D4CF9B242935405200DDB5F0 /* HomePageLinkOptionView.swift in Sources */, D4607641292AB18D00DE9590 /* HomePageView.swift in Sources */, diff --git a/tumble-ios/Core/Extensions/FontExstensions.swift b/tumble-ios/Core/Extensions/FontExtensions.swift similarity index 76% rename from tumble-ios/Core/Extensions/FontExstensions.swift rename to tumble-ios/Core/Extensions/FontExtensions.swift index 57d05959..22118075 100644 --- a/tumble-ios/Core/Extensions/FontExstensions.swift +++ b/tumble-ios/Core/Extensions/FontExtensions.swift @@ -13,7 +13,10 @@ extension Font { static let mediumSmallFont = Font.custom("roboto_regular", size: Font.TextStyle.footnote.size, relativeTo: .caption) static let smallFont = Font.custom("roboto_regular", size: Font.TextStyle.caption.size, relativeTo: .caption) static let verySmallFont = Font.custom("roboto_regular", size: Font.TextStyle.caption2.size, relativeTo: .caption) - static let drawerItemFont = Font.custom("roboto_regular", size: 16, relativeTo: .caption) + static let smallIconFont = Font.custom("roboto_regular", size: 14, relativeTo: .caption) + static let mediumIconFont = Font.custom("roboto_regular", size: 16, relativeTo: .caption) + static let largeIconFont = Font.custom("roboto_regular", size: 18, relativeTo: .caption) + static let veryLargeIconFont = Font.custom("roboto_regular", size: 20, relativeTo: .caption) } extension Font.TextStyle { diff --git a/tumble-ios/Core/Extensions/ScheduleExtensions.swift b/tumble-ios/Core/Extensions/ScheduleExtensions.swift index 2d55d2da..f2bd2ae8 100644 --- a/tumble-ios/Core/Extensions/ScheduleExtensions.swift +++ b/tumble-ios/Core/Extensions/ScheduleExtensions.swift @@ -11,6 +11,8 @@ import SwiftUI //To convert API result date (ISO8601) to `Date`, this property should not be inside any methods let inDateFormatter = ISO8601DateFormatter() + + extension [API.Types.Response.Schedule] { func flatten() -> [DayUiModel] { @@ -18,9 +20,7 @@ extension [API.Types.Response.Schedule] { var days: [DayUiModel] = [] self.forEach { schedule in days.append(contentsOf: schedule.days.reduce(into: []) { - let day = Calendar.current.dateComponents([.day], from: inDateFormatter.date(from: $1.isoString)!).day! - let today = Calendar.current.dateComponents([.day], from: Date.now).day! - if day >= today {$0.append($1)}}.toUiModel()) + if $1.isValidDay() {$0.append($1)}}.toUiModel()) } return days.toOrderedDayUiModels() } @@ -49,20 +49,31 @@ extension [API.Types.Response.Schedule] { } extension API.Types.Response.Schedule { - func assignRandomCoursesColors() -> [String : [String : Color]] { - var coursesColors: [String : [String : Color]] = [:] + + func assignCoursesRandomColors() -> [String : [String : Color]] { + var courseColors: [String : [String : Color]] = [:] for day in self.days { for event in day.events { - if coursesColors[event.course.id] == nil { + if courseColors[event.course.id] == nil { let hexColorString = colors.randomElement()!; - coursesColors[event.course.id] = [hexColorString : hexStringToUIColor(hex: hexColorString)] + courseColors[event.course.id] = [hexColorString : hexStringToUIColor(hex: hexColorString)] } } } - return coursesColors + return courseColors } - + func courses() -> [String] { + var courses: [String] = [] + for day in self.days { + for event in day.events { + if !courses.contains(event.course.id) { + courses.append(event.course.id) + } + } + } + return courses + } } extension [DayUiModel] { @@ -90,39 +101,34 @@ extension [DayUiModel] { } } + extension [API.Types.Response.Day] { func toUiModel() -> [DayUiModel] { return self.map { day in return DayUiModel(name: day.name, date: day.date, isoString: day.isoString, weekNumber: day.weekNumber, events: day.events) } } - - + + // Used in ScheduleMainPageView when loading a schedule + // from the local database and converting into a list of UI models func toOrderedDays() -> [DayUiModel] { inDateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] var days: [DayUiModel] = [] days.append(contentsOf: self.reduce(into: []) { - let day = Calendar.current.dateComponents([.day], from: inDateFormatter.date(from: $1.isoString)!).day! - let today = Calendar.current.dateComponents([.day], from: Date.now).day! - if day >= today {$0.append($1)}}.toUiModel()) + if $1.isValidDay() {$0.append($1)}}.toUiModel()) return days.toOrderedDayUiModels() } } -extension API.Types.Response.Event { - func color() -> Color { - var hexColor: String = "" - CourseColorStore.load { result in - switch result { - case .failure(_): - print("Error on course with id: \(self.id)") - case .success(let courses): - if !courses.isEmpty { - print(course.id) - hexColor = courses[course.id]! - } - } - } - return hexColor == "" ? hexStringToUIColor(hex: colors.randomElement() ?? "FFFFFF") : hexStringToUIColor(hex: hexColor) +extension API.Types.Response.Day { + // Determines if a schedule should be shown in the UI view based + // on whether or not the day of the month has already passed or not + func isValidDay() -> Bool { + let dayIsoString: String = self.isoString + let day: Int = Calendar.current.dateComponents([.day], from: inDateFormatter.date(from: dayIsoString)!).day! + let today: Int = Calendar.current.dateComponents([.day], from: inDateFormatter.date(from: dayIsoString)!).day! + let month: Int = Calendar.current.dateComponents([.month], from: Date.now).month! + let todaysMonth: Int = Calendar.current.dateComponents([.month], from: Date.now).month! + return ((day >= today && month == todaysMonth) || (month != todaysMonth)) } } diff --git a/tumble-ios/Core/Utilities/Color.swift b/tumble-ios/Core/Utilities/Color.swift index 65a9bc23..79a1ba4b 100644 --- a/tumble-ios/Core/Utilities/Color.swift +++ b/tumble-ios/Core/Utilities/Color.swift @@ -29,24 +29,3 @@ func hexStringToUIColor (hex:String) -> Color { ) } -func assignCardColor(event: API.Types.Response.Event) -> String { - var cardColor: String = "" - let group = DispatchGroup() - group.enter() - CourseColorStore.load { result in - DispatchQueue.main.async { - switch result { - case .failure(_): - print("Error on course with id: \(event.course.id)") - case .success(let courses): - if !courses.isEmpty { - let hexColor = courses[event.course.id]!; - cardColor = hexColor == "" ? colors.randomElement() ?? "FFFFFF" : hexColor - } - } - } - group.leave() - } - group.wait() - return cardColor -} diff --git a/tumble-ios/Core/ViewModels/HomePageView-ViewModel.swift b/tumble-ios/Core/ViewModels/HomePageView-ViewModel.swift index 1607530c..bd27b0f0 100644 --- a/tumble-ios/Core/ViewModels/HomePageView-ViewModel.swift +++ b/tumble-ios/Core/ViewModels/HomePageView-ViewModel.swift @@ -44,7 +44,6 @@ extension HomePageView { } let schoolUrl: String = schools.first(where: {$0.name == school!.name})!.schoolUrl - print(schoolUrl) return schoolUrl } diff --git a/tumble-ios/Core/ViewModels/SchedulePageMainView-ViewModel.swift b/tumble-ios/Core/ViewModels/SchedulePageMainView-ViewModel.swift index 4468280e..9a385597 100644 --- a/tumble-ios/Core/ViewModels/SchedulePageMainView-ViewModel.swift +++ b/tumble-ios/Core/ViewModels/SchedulePageMainView-ViewModel.swift @@ -26,9 +26,8 @@ extension ScheduleMainPageView { @MainActor class ScheduleMainPageViewModel: ObservableObject { @Published var scheduleViewTypes: [ScheduleViewType] = ScheduleViewType.allValues @Published var status: ScheduleMainPageStatus = .loading - @Published var schedules: [API.Types.Response.Schedule] = [] @Published var days: [DayUiModel] = [] - @Published var courseColors: [String : String] = [:] + @Published var courseColors: CourseAndColorDict = [:] @Published var viewType: ScheduleViewType = { let hasView: Bool = UserDefaults.standard.isKeyPresentInUserDefaults(key: UserDefaults.StoreKey.viewType.rawValue) if !(hasView) { diff --git a/tumble-ios/Core/ViewModels/SearchView-ViewModel.swift b/tumble-ios/Core/ViewModels/SearchView-ViewModel.swift index d0cf8768..25501383 100644 --- a/tumble-ios/Core/ViewModels/SearchView-ViewModel.swift +++ b/tumble-ios/Core/ViewModels/SearchView-ViewModel.swift @@ -16,7 +16,7 @@ enum SearchStatus { case empty } -enum PreviewDelegateStatus { +enum SchedulePreviewStatus { case loaded case loading case error @@ -34,10 +34,10 @@ extension SearchParentView { @Published var scheduleForPreview: API.Types.Response.Schedule? = nil @Published var scheduleListOfDays: [DayUiModel]? = nil @Published var presentPreview: Bool = false - @Published var previewDelegateStatus: PreviewDelegateStatus = .loading + @Published var schedulePreviewStatus: SchedulePreviewStatus = .loading @Published var school: School? = UserDefaults.standard.getDefaultSchool() @Published var schedulePreviewIsSaved: Bool = false - @Published var courseColors: [String : String] = [:] + @Published var courseColors: CourseAndColorDict = [:] private var client: API.Client = API.Client.shared @@ -59,19 +59,48 @@ extension SearchParentView { } } + // Handles child modal status, [.loaded, .loading, .error, .empty] + func onLoadSchedule(programme: API.Types.Response.Programme) -> Void { + self.schedulePreviewIsSaved = false + self.schedulePreviewStatus = .loading + self.presentPreview = true + self.checkSavedSchedule(scheduleId: programme.id) + client.get(.schedule(scheduleId: programme.id, schoolId: String(school!.id))) { (result: Result) in + DispatchQueue.main.async { + switch result { + case .success(let result): + self.scheduleForPreview = result + self.scheduleListOfDays = result.days.toOrderedDays() + self.initCourseColors() + self.presentPreview = true + self.schedulePreviewStatus = .loaded + case .failure(_): + self.schedulePreviewStatus = .error + } + } + } + } + + func getCourseColors() -> [String : [String : Color]] { + return courseColors.reduce(into: [:]) { (coursesAndColorsDict, course) in + let (courseId, color) = course + coursesAndColorsDict[courseId, default: [:]][color] = hexStringToUIColor(hex: color) + } + } + + func onBookmark(courseColors: [String : [String : Color]]) -> Void { // If the schedule isn't already saved in the local database if !self.schedulePreviewIsSaved { - print("Saving schedule") - ScheduleStore.save(schedule: self.scheduleForPreview!) { result in + ScheduleStore.save(schedule: self.scheduleForPreview!) { scheduleResult in DispatchQueue.main.async { - if case .failure(let error) = result { + if case .failure(let error) = scheduleResult { fatalError(error.localizedDescription) } else { print("Saving schedule") self.schedulePreviewIsSaved = true - CourseColorStore.save(newCourses: courseColors) { result in - if case .failure(let error) = result { + CourseColorStore.save(coursesAndColors: courseColors) { courseResult in + if case .failure(let error) = courseResult { fatalError(error.localizedDescription) } else { print("Applying course colors ...") @@ -91,6 +120,13 @@ extension SearchParentView { } else { print("Removed schedule") self.schedulePreviewIsSaved = false + CourseColorStore.remove(removeCourses: self.scheduleForPreview!.courses()) { result in + if case .failure(let error) = result { + fatalError(error.localizedDescription) + } else { + print("Removed course colors ...") + } + } } } } @@ -103,9 +139,9 @@ extension SearchParentView { client.get(.searchProgramme(searchQuery: searchQuery, schoolId: String(school!.id))) { (result: Result) in DispatchQueue.main.async { switch result { - case .success(let success): + case .success(let result): self.status = SearchStatus.loading - self.parseSearchResults(success) + self.parseSearchResults(result) case .failure( _): self.status = SearchStatus.error print("error") @@ -123,27 +159,6 @@ extension SearchParentView { self.searchBarText = "" } - func onLoadSchedule(programme: API.Types.Response.Programme) -> Void { - self.schedulePreviewIsSaved = false - self.previewDelegateStatus = .loading - self.presentPreview = true - self.checkSavedSchedule(scheduleId: programme.id) - client.get(.schedule(scheduleId: programme.id, schoolId: String(school!.id))) { (result: Result) in - DispatchQueue.main.async { - switch result { - case .success(let schedule): - self.scheduleForPreview = schedule - self.scheduleListOfDays = schedule.days.toOrderedDays() - self.initCourseColors() - self.presentPreview = true - self.previewDelegateStatus = .loaded - case .failure(_): - self.previewDelegateStatus = .error - } - } - } - } - func initCourseColors() -> Void { for day in self.scheduleListOfDays! { for event in day.events { diff --git a/tumble-ios/Core/Views/AppNavigation/AppNavigatorView.swift b/tumble-ios/Core/Views/AppNavigation/AppNavigatorView.swift index 7eaf0356..7beeb700 100644 --- a/tumble-ios/Core/Views/AppNavigation/AppNavigatorView.swift +++ b/tumble-ios/Core/Views/AppNavigation/AppNavigatorView.swift @@ -68,23 +68,10 @@ struct AppNavigatorView: View { .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarLeading, content: { - Button(action: { - viewModel.onToggleDrawer() - }, label: { - Image(systemName: viewModel.menuOpened ? "xmark" : "line.3.horizontal") - .font(.system(size: 17)) - .foregroundColor(Color("OnBackground")) - }) + DrawerButtonView(onToggleDrawer: onToggleDrawer, menuOpened: viewModel.menuOpened) }) ToolbarItem(placement: .navigationBarTrailing, content: { - NavigationLink(destination: - SearchParentView() - .navigationBarBackButtonHidden(true) - .navigationBarItems(leading: BackButton()), label: { - Image(systemName: "magnifyingglass") - .font(.system(size: 17)) - .foregroundColor(Color("OnBackground")) - }) + SearchButtonView() }) ToolbarItemGroup(placement: .bottomBar, content: { BottomBarView(onChangeTab: onChangeTab).environmentObject(viewModel) @@ -125,6 +112,10 @@ struct AppNavigatorView: View { func onChangeTab(tab: TabType) -> Void { viewModel.onChangeTab(tab: tab) } + + func onToggleDrawer() -> Void { + viewModel.onToggleDrawer() + } } diff --git a/tumble-ios/Core/Views/AppNavigation/DrawerButtonView.swift b/tumble-ios/Core/Views/AppNavigation/DrawerButtonView.swift new file mode 100644 index 00000000..fcc8b9ef --- /dev/null +++ b/tumble-ios/Core/Views/AppNavigation/DrawerButtonView.swift @@ -0,0 +1,25 @@ +// +// DrawerButtonView.swift +// tumble-ios +// +// Created by Adis Veletanlic on 2023-01-26. +// + +import SwiftUI + +typealias OnToggleDrawer = () -> Void + +struct DrawerButtonView: View { + let onToggleDrawer: OnToggleDrawer + let menuOpened: Bool + var body: some View { + Button(action: { + onToggleDrawer() + }, label: { + Image(systemName: menuOpened ? "xmark" : "line.3.horizontal") + .font(.system(size: 17)) + .foregroundColor(Color("OnBackground")) + }) + } +} + diff --git a/tumble-ios/Core/Views/AppNavigation/SearchButtonView.swift b/tumble-ios/Core/Views/AppNavigation/SearchButtonView.swift new file mode 100644 index 00000000..b381ade6 --- /dev/null +++ b/tumble-ios/Core/Views/AppNavigation/SearchButtonView.swift @@ -0,0 +1,27 @@ +// +// SearchButtonView.swift +// tumble-ios +// +// Created by Adis Veletanlic on 2023-01-26. +// + +import SwiftUI + +struct SearchButtonView: View { + var body: some View { + NavigationLink(destination: + SearchParentView() + .navigationBarBackButtonHidden(true) + .navigationBarItems(leading: BackButton()), label: { + Image(systemName: "magnifyingglass") + .font(.system(size: 17)) + .foregroundColor(Color("OnBackground")) + }) + } +} + +struct SearchButtonView_Previews: PreviewProvider { + static var previews: some View { + SearchButtonView() + } +} diff --git a/tumble-ios/Core/Views/HomePage/HomePageLinkOptionView.swift b/tumble-ios/Core/Views/HomePage/HomePageLinkOptionView.swift index 33935010..a28174f6 100644 --- a/tumble-ios/Core/Views/HomePage/HomePageLinkOptionView.swift +++ b/tumble-ios/Core/Views/HomePage/HomePageLinkOptionView.swift @@ -16,7 +16,7 @@ struct HomePageLinkOptionView: View { HStack (spacing: 0) { HStack { Image(systemName: link.image) - .font(.system(size: 17)) + .font(.largeIconFont) .frame(width: 17, height: 17) .padding(15) .foregroundColor(Color("OnPrimary")) diff --git a/tumble-ios/Core/Views/HomePage/HomePageLinkSectionView.swift b/tumble-ios/Core/Views/HomePage/HomePageLinkSectionView.swift index f5653ff7..da460829 100644 --- a/tumble-ios/Core/Views/HomePage/HomePageLinkSectionView.swift +++ b/tumble-ios/Core/Views/HomePage/HomePageLinkSectionView.swift @@ -39,11 +39,11 @@ struct HomePageLinkSectionView: View { Text(title) .font(.title2) .foregroundColor(Color("OnBackground")) - .bold() + //.bold() .padding(.leading, 15) Spacer() Image(systemName: image) - .font(.system(size: 20)) + .font(.veryLargeIconFont) .padding(.trailing, 20) .foregroundColor(Color("OnBackground")) } diff --git a/tumble-ios/Core/Views/HomePage/HomePageQuickAccessOptionView.swift b/tumble-ios/Core/Views/HomePage/HomePageQuickAccessOptionView.swift index 60627ef3..1c0c0708 100644 --- a/tumble-ios/Core/Views/HomePage/HomePageQuickAccessOptionView.swift +++ b/tumble-ios/Core/Views/HomePage/HomePageQuickAccessOptionView.swift @@ -16,7 +16,7 @@ struct HomePageQuickAccessOptionView: View { HStack (spacing: 0) { HStack { Image(systemName: option.image) - .font(.system(size: 17)) + .font(.largeIconFont) .frame(width: 17, height: 17) .padding(15) .foregroundColor(Color("OnPrimary")) diff --git a/tumble-ios/Core/Views/HomePage/HomePageQuickAccessSectionView.swift b/tumble-ios/Core/Views/HomePage/HomePageQuickAccessSectionView.swift index 26bd5f54..5ed3c06e 100644 --- a/tumble-ios/Core/Views/HomePage/HomePageQuickAccessSectionView.swift +++ b/tumble-ios/Core/Views/HomePage/HomePageQuickAccessSectionView.swift @@ -31,11 +31,11 @@ struct HomePageQuickAccessSectionView: View { Text(title) .font(.title2) .foregroundColor(Color("OnBackground")) - .bold() + //.bold() .padding(.leading, 15) Spacer() Image(systemName: image) - .font(.system(size: 20)) + .font(.veryLargeIconFont) .padding(.trailing, 20) .foregroundColor(Color("OnBackground")) } diff --git a/tumble-ios/Core/Views/HomePage/HomePageView.swift b/tumble-ios/Core/Views/HomePage/HomePageView.swift index e71ecf5c..9f9f7443 100644 --- a/tumble-ios/Core/Views/HomePage/HomePageView.swift +++ b/tumble-ios/Core/Views/HomePage/HomePageView.swift @@ -17,8 +17,6 @@ struct HomePageView: View { @StateObject var viewModel: HomePageViewModel = HomePageViewModel() let backgroundColor: Color = Color("PrimaryColor").opacity(0.75) let iconColor: Color = Color("PrimaryColor").opacity(0.95) - @State private var uniColor: Color = Color.black - @State private var uniLink: String = "schema.hkr.se" var body: some View { ScrollView { @@ -27,8 +25,8 @@ struct HomePageView: View { QuickAccessOption(id: .exams, title: "Registered exams", image: "text.badge.checkmark", onClick: {}, backgroundColor: backgroundColor, iconColor: iconColor), QuickAccessOption(id: .schedules, title: "View a schedule", image: "list.clipboard", onClick: {}, backgroundColor: backgroundColor, iconColor: iconColor) ]) - .padding(.top, 20) - + .padding(.top, 30) + HomePageLinkSectionView(title: "Links", image: "link", links: [ ExternalLink(id: .university, title: viewModel.getUniversityName(), image: "paperclip", backgroundColor: viewModel.getUniversityColor(), iconColor: viewModel.getUniversityColor(), url: viewModel.getUniversityUrl()), ExternalLink(id: .canvas, title: "Canvas", image: "paperclip", backgroundColor: .red, iconColor: .red, url: viewModel.getCanvasUrl()), @@ -37,10 +35,6 @@ struct HomePageView: View { ]) .padding(.top, 20) } - } - - func redrawUniSpecifics() -> Void { - uniColor = viewModel.getUniversityColor() - uniLink = viewModel.getUniversityUrl() + .padding([.leading, .trailing], 2.5) } } diff --git a/tumble-ios/Core/Views/Schedule/List/CardBannerView.swift b/tumble-ios/Core/Views/Schedule/List/CardBannerView.swift new file mode 100644 index 00000000..1a0dfe66 --- /dev/null +++ b/tumble-ios/Core/Views/Schedule/List/CardBannerView.swift @@ -0,0 +1,43 @@ +// +// CardBannerView.swift +// tumble-ios +// +// Created by Adis Veletanlic on 2023-01-26. +// + +import SwiftUI + +struct CardBannerView: View { + let color: Color + let timeSpan: String + let isSpecial: Bool + let courseName: String + let isDisclosed: Bool + var body: some View { + HStack { + Circle() + .foregroundColor(color) + .frame(height: 7) + Text(timeSpan) + .font(.subheadline) + .foregroundColor(Color("OnSurface")) + Spacer() + if !isDisclosed { + Text(courseName) + .font(.subheadline) + .foregroundColor(Color("OnSurface")) + .padding(.trailing, 10) + } + if isSpecial { + Image(systemName: "person.crop.circle.badge.exclamationmark") + .font(.title3) + .foregroundColor(Color("OnSurface")) + .padding(.trailing, 15) + } + } + .padding(.top, 20) + .padding(.leading, 25) + .padding(.bottom, 10) + } +} + diff --git a/tumble-ios/Core/Views/Schedule/List/ScheduleCardView.swift b/tumble-ios/Core/Views/Schedule/List/ScheduleCardView.swift index 0e41f2f1..41017354 100644 --- a/tumble-ios/Core/Views/Schedule/List/ScheduleCardView.swift +++ b/tumble-ios/Core/Views/Schedule/List/ScheduleCardView.swift @@ -9,6 +9,7 @@ import SwiftUI struct ScheduleCardView: View { @AppStorage(UserDefaults.StoreKey.theme.rawValue) private var isDarkMode = false + @State private var isDisclosed: Bool = false let event: API.Types.Response.Event let isLast: Bool let color: Color @@ -22,59 +23,28 @@ struct ScheduleCardView: View { .offset(x: 10) .cornerRadius(8, corners: [.topRight, .bottomRight]) VStack (alignment: .leading, spacing: 0) { - HStack { - Circle() - .foregroundColor(event.isSpecial ? .red : color) - .frame(height: 7) - Text("\(event.from.ISOtoHours()) - \(event.to.ISOtoHours())") - .font(.subheadline) - .foregroundColor(Color("OnSurface")) - Spacer() - if event.isSpecial { - Image(systemName: "person.crop.circle.badge.exclamationmark") - .font(.title3) - .foregroundColor(Color("OnSurface")) - .padding(.trailing, 15) - } - } - .padding(.top, 20) - .padding(.leading, 25) - .padding(.bottom, 10) - VStack (alignment: .leading) { - Text(event.title) - .font(.title2) - .foregroundColor(Color("OnSurface")) - .padding(.leading, 25) - .padding(.trailing, 25) - .padding(.bottom, 2.5) - VStack { - Text(event.course.englishName.trimmingCharacters(in: .whitespaces)) - .font(.title3) - .foregroundColor(Color("OnSurface")) - .padding(.leading, 25) - .padding(.bottom, 10) - Spacer() - } - HStack { - Spacer() - Text(event.locations.first?.id ?? "Unknown") - .font(.title3) - .foregroundColor(Color("OnSurface")) - Image(systemName: "location") - .font(.title3) - .foregroundColor(Color("OnSurface")) - .padding(.trailing, 5) - } - .padding(.trailing, 10) - Spacer() + CardBannerView(color: event.isSpecial ? .red : color, timeSpan: "\(event.from.ISOtoHours()) - \(event.to.ISOtoHours())", isSpecial: event.isSpecial, courseName: event.course.englishName, isDisclosed: isDisclosed) + + if isDisclosed { + CardInformationView(title: event.title, courseName: event.course.englishName.trimmingCharacters(in: .whitespaces), location: event.locations.first?.id ?? "Unknown") + } Spacer() } } - .frame(height: 150) - .padding(.leading, 20) - .padding(.trailing, 20) + .onTapGesture { + withAnimation { + isDisclosed.toggle() + } + } + .onLongPressGesture { + print("long press!") + } + + .frame(height: isDisclosed ? 145 : 50) + .padding(.leading, 8) + .padding(.trailing, 8) .padding(.bottom, isLast ? 40 : 0) } } diff --git a/tumble-ios/Core/Views/Schedule/List/ScheduleListView.swift b/tumble-ios/Core/Views/Schedule/List/ScheduleListView.swift index 58260a43..96e3d17f 100644 --- a/tumble-ios/Core/Views/Schedule/List/ScheduleListView.swift +++ b/tumble-ios/Core/Views/Schedule/List/ScheduleListView.swift @@ -9,7 +9,7 @@ import SwiftUI struct ScheduleListView: View { let days: [DayUiModel] - let courseColors: [String : String] + let courseColors: CourseAndColorDict var body: some View { ZStack { ScrollView { @@ -19,6 +19,7 @@ struct ScheduleListView: View { Section(header: DayHeaderSectionView(day: day), content: { ForEach(day.events, id: \.id) { event in ScheduleCardView(event: event, isLast: event == day.events.last, color: hexStringToUIColor(hex: courseColors[event.course.id] ?? "FFFFFF")) + .animation(.default) } }) .padding(.top, 35) diff --git a/tumble-ios/Core/Views/Search/Preview/PreviewCardBannerView.swift b/tumble-ios/Core/Views/Search/Preview/PreviewCardBannerView.swift new file mode 100644 index 00000000..5d53e594 --- /dev/null +++ b/tumble-ios/Core/Views/Search/Preview/PreviewCardBannerView.swift @@ -0,0 +1,35 @@ +// +// PreviewCardBannerView.swift +// tumble-ios +// +// Created by Adis Veletanlic on 2023-01-27. +// + +import SwiftUI + +struct PreviewCardBannerView: View { + let color: Color + let timeSpan: String + let isSpecial: Bool + var body: some View { + HStack { + Circle() + .foregroundColor(color) + .frame(height: 7) + Text(timeSpan) + .font(.subheadline) + .foregroundColor(Color("OnSurface")) + Spacer() + if isSpecial { + Image(systemName: "person.crop.circle.badge.exclamationmark") + .font(.title3) + .foregroundColor(Color("OnSurface")) + .padding(.trailing, 15) + } + } + .padding(.top, 20) + .padding(.leading, 25) + .padding(.bottom, 10) + } +} + diff --git a/tumble-ios/Core/Views/Search/Preview/SchedulePreviewCardView.swift b/tumble-ios/Core/Views/Search/Preview/SchedulePreviewCardView.swift index 7e593099..3ab8becf 100644 --- a/tumble-ios/Core/Views/Search/Preview/SchedulePreviewCardView.swift +++ b/tumble-ios/Core/Views/Search/Preview/SchedulePreviewCardView.swift @@ -22,57 +22,13 @@ struct SchedulePreviewCardView: View { .offset(x: 10) .cornerRadius(8, corners: [.topRight, .bottomRight]) VStack (alignment: .leading, spacing: 0) { - HStack { - Circle() - .foregroundColor(event.isSpecial ? .red : previewColor) - .frame(height: 7) - Text("\(event.from.ISOtoHours()) - \(event.to.ISOtoHours())") - .font(.subheadline) - .foregroundColor(Color("OnSurface")) - Spacer() - if event.isSpecial { - Image(systemName: "person.crop.circle.badge.exclamationmark") - .font(.title3) - .foregroundColor(Color("OnSurface")) - .padding(.trailing, 15) - } - } - .padding(.top, 20) - .padding(.leading, 25) - .padding(.bottom, 10) - VStack (alignment: .leading) { - Text(event.title) - .font(.title2) - .foregroundColor(Color("OnSurface")) - .padding(.leading, 25) - .padding(.trailing, 25) - .padding(.bottom, 2.5) - VStack { - Text(event.course.englishName.trimmingCharacters(in: .whitespaces)) - .font(.title3) - .foregroundColor(Color("OnSurface")) - .padding(.leading, 25) - .padding(.bottom, 10) - Spacer() - } - HStack { - Spacer() - Text(event.locations.first?.id ?? "Unknown") - .font(.title3) - .foregroundColor(Color("OnSurface")) - Image(systemName: "location") - .font(.title3) - .foregroundColor(Color("OnSurface")) - .padding(.trailing, 5) - } - .padding(.trailing, 10) - Spacer() - } + PreviewCardBannerView(color: event.isSpecial ? .red : previewColor, timeSpan: "\(event.from.ISOtoHours()) - \(event.to.ISOtoHours())", isSpecial: event.isSpecial) + PreviewCardInformationView(title: event.title, courseName: event.course.englishName.trimmingCharacters(in: .whitespaces), location: event.locations.first?.id ?? "Unknown") Spacer() } } - .frame(height: 155) + .frame(height: 145) .padding(.leading, 20) .padding(.trailing, 20) .padding(.bottom, isLast ? 40 : 0) diff --git a/tumble-ios/Core/Views/Search/Preview/SchedulePreviewView.swift b/tumble-ios/Core/Views/Search/Preview/SchedulePreviewView.swift index 338ee8e8..c06c9576 100644 --- a/tumble-ios/Core/Views/Search/Preview/SchedulePreviewView.swift +++ b/tumble-ios/Core/Views/Search/Preview/SchedulePreviewView.swift @@ -10,18 +10,18 @@ import SwiftUI struct SchedulePreviewView: View { @EnvironmentObject var parentViewModel: SearchParentView.SearchViewModel var body: some View { - if (parentViewModel.previewDelegateStatus == .loaded) { - let courseColors = parentViewModel.scheduleForPreview!.assignRandomCoursesColors(); + if (parentViewModel.schedulePreviewStatus == .loaded) { + let courseColors = parentViewModel.scheduleForPreview!.assignCoursesRandomColors() SchedulePreviewListView(toggled: parentViewModel.schedulePreviewIsSaved, randomCourseColors: courseColors, existingCourseColors: parentViewModel.courseColors, days: parentViewModel.scheduleListOfDays!) { parentViewModel.onBookmark(courseColors: courseColors) } } - if (parentViewModel.previewDelegateStatus == .loading) { + if (parentViewModel.schedulePreviewStatus == .loading) { Spacer() CustomProgressView() Spacer() } - if (parentViewModel.previewDelegateStatus == .error || parentViewModel.previewDelegateStatus == .empty) { + if (parentViewModel.schedulePreviewStatus == .error || parentViewModel.schedulePreviewStatus == .empty) { Text("Error!") } } diff --git a/tumble-ios/Services/API/Backend/APIClient.swift b/tumble-ios/Services/API/Backend/APIClient.swift index fb7c7a15..4800eb95 100644 --- a/tumble-ios/Services/API/Backend/APIClient.swift +++ b/tumble-ios/Services/API/Backend/APIClient.swift @@ -38,7 +38,6 @@ extension API { if let data = data { do { let result = try self.decoder.decode(Response.self, from: data) - print("Success") callback?(.success(result)) } catch { print("Decoding error: \(error)") @@ -53,7 +52,6 @@ extension API { // [HTTP GET] func get(_ endpoint: Types.Endpoint, then callback: ((Result) -> Void)? = nil ) where Response: Codable { - print(endpoint.url) let body: Types.Request.Empty? = nil fetch(endpoint, method: .get, body: body) { result in callback?(result) diff --git a/tumble-ios/Services/API/Database/CourseColorStore.swift b/tumble-ios/Services/API/Database/CourseColorStore.swift index f687572b..1d2ef366 100644 --- a/tumble-ios/Services/API/Database/CourseColorStore.swift +++ b/tumble-ios/Services/API/Database/CourseColorStore.swift @@ -8,10 +8,7 @@ import Foundation import SwiftUI -struct Course: Codable { - let id: String - let hexColor: String -} +typealias CourseAndColorDict = [String : String] class CourseColorStore: ObservableObject { private static func fileURL() throws -> URL { @@ -22,7 +19,7 @@ class CourseColorStore: ObservableObject { .appendingPathComponent("colors.data") } - static func load(completion: @escaping (Result<[String : String], Error>)->Void) { + static func load(completion: @escaping (Result)->Void) { DispatchQueue.global(qos: .background).async { do { let fileURL = try fileURL() @@ -32,7 +29,7 @@ class CourseColorStore: ObservableObject { } return } - let courses = try JSONDecoder().decode([String : String].self, from: file.availableData) + let courses = try JSONDecoder().decode(CourseAndColorDict.self, from: file.availableData) DispatchQueue.main.async { completion(.success(courses)) } @@ -43,8 +40,15 @@ class CourseColorStore: ObservableObject { } } } - - static func save(newCourses: [String: [String : Color]], completion: @escaping (Result)->Void) { + + // [String : [String : Color]] is a dictionary of + // course names with its respective dictionary of hexColor: String, and color: Color + // { + // "course_one" : { + // "#45F327" : Color.blue + // } + // } + static func save(coursesAndColors: [String : [String : Color]], completion: @escaping (Result)->Void) { DispatchQueue.global(qos: .background).async { do { let fileURL = try fileURL() @@ -56,13 +60,14 @@ class CourseColorStore: ObservableObject { } case .success(let courses): do { - var newCourseColorDict: [String : String] = [:]; - for (course, colors) in newCourses { - for (hexColor, _) in colors { - newCourseColorDict[course] = hexColor; + var newCourseColorsDict: CourseAndColorDict = [:] + for (course, colorDict) in coursesAndColors { + for (hexColor, _) in colorDict { + newCourseColorsDict[course] = hexColor; } } - let finalCourseColorDict = courses.merging(newCourseColorDict, uniquingKeysWith: +) + + let finalCourseColorDict = courses.merging(newCourseColorsDict) { (_, new) in new } let data = try JSONEncoder().encode(finalCourseColorDict) try data.write(to: fileURL) DispatchQueue.main.async { @@ -93,7 +98,7 @@ class CourseColorStore: ObservableObject { } return } - var courses = try JSONDecoder().decode([String : String].self, from: file.availableData) + var courses = try JSONDecoder().decode(CourseAndColorDict.self, from: file.availableData) courses.removeAll() @@ -111,7 +116,7 @@ class CourseColorStore: ObservableObject { } } - static func remove(courseId: String, completion: @escaping (Result)->Void) { + static func remove(removeCourses: [String], completion: @escaping (Result)->Void) { DispatchQueue.global(qos: .background).async { do { let fileURL = try fileURL() @@ -121,10 +126,11 @@ class CourseColorStore: ObservableObject { } return } - var courses = try JSONDecoder().decode([Course].self, from: file.availableData) - + var courses = try JSONDecoder().decode(CourseAndColorDict.self, from: file.availableData) - courses.removeAll(where: {$0.id == courseId}) + for courseId in removeCourses { + courses.removeValue(forKey: courseId) + } let data = try JSONEncoder().encode(courses) try data.write(to: fileURL) diff --git a/tumble-ios/Services/API/Database/ScheduleStore.swift b/tumble-ios/Services/API/Database/ScheduleStore.swift index f774234d..e940bd1b 100644 --- a/tumble-ios/Services/API/Database/ScheduleStore.swift +++ b/tumble-ios/Services/API/Database/ScheduleStore.swift @@ -46,7 +46,7 @@ class ScheduleStore: ObservableObject { switch result { case .failure(let error): DispatchQueue.main.async { - print("failed to save \(schedule.id)") + print("Failed to save \(schedule.id)") completion(.failure(error)) } case .success(let schedules):