From 797f0e7f22062d2f7ca8db94c040d16e2a9b7b3e Mon Sep 17 00:00:00 2001 From: Ivan Magda Date: Mon, 8 Feb 2021 12:54:15 +0300 Subject: [PATCH 1/5] Update submodule registration --- .../Modules/BaseExplore/BaseExploreView.swift | 6 ++++- .../BaseExploreViewController.swift | 27 +++++++++++++------ .../Modules/Home/HomeViewController.swift | 19 +++++++++---- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/Stepic/Sources/Modules/BaseExplore/BaseExploreView.swift b/Stepic/Sources/Modules/BaseExplore/BaseExploreView.swift index 77428c980c..cfe678e311 100644 --- a/Stepic/Sources/Modules/BaseExplore/BaseExploreView.swift +++ b/Stepic/Sources/Modules/BaseExplore/BaseExploreView.swift @@ -29,7 +29,11 @@ final class BaseExploreView: UIView { } func removeBlockView(_ view: UIView) { - self.scrollableStackView.removeArrangedView(view) + if self.scrollableStackView.arrangedSubviews.contains(view) { + self.scrollableStackView.removeArrangedView(view) + } else { + view.removeFromSuperview() + } } func insertBlockView(_ view: UIView, at position: Int) { diff --git a/Stepic/Sources/Modules/BaseExplore/BaseExploreViewController.swift b/Stepic/Sources/Modules/BaseExplore/BaseExploreViewController.swift index d0c8b4e59f..74de923113 100644 --- a/Stepic/Sources/Modules/BaseExplore/BaseExploreViewController.swift +++ b/Stepic/Sources/Modules/BaseExplore/BaseExploreViewController.swift @@ -47,20 +47,29 @@ class BaseExploreViewController: UIViewController { // MARK: Modules func registerSubmodule(_ submodule: Submodule) { + defer { + submodule.viewController?.didMove(toParent: self) + } + self.submodules.append(submodule) if let viewController = submodule.viewController { self.addChild(viewController) } - // We have contract here: - // - subviews in exploreView have same position as in corresponding Submodule object - for module in self.submodules where module.type.position >= submodule.type.position { - self.exploreView?.insertBlockView( - submodule.view, - before: module.view - ) - return + if submodule.isArrangeable { + let arrangeableSubmodules = self.submodules.filter(\.isArrangeable) + // We have contract here: + // - subviews in exploreView have same position as in corresponding Submodule object + for module in arrangeableSubmodules where module.type.position >= submodule.type.position { + self.exploreView?.insertBlockView( + submodule.view, + before: module.view + ) + return + } + } else { + self.exploreView?.addSubview(submodule.view) } } @@ -71,6 +80,7 @@ class BaseExploreViewController: UIViewController { } func removeSubmodule(_ submodule: Submodule) { + submodule.viewController?.willMove(toParent: nil) self.exploreView?.removeBlockView(submodule.view) submodule.viewController?.removeFromParent() self.submodules = self.submodules.filter { submodule.view != $0.view } @@ -124,6 +134,7 @@ class BaseExploreViewController: UIViewController { struct Submodule { let viewController: UIViewController? let view: UIView + var isArrangeable: Bool = true let isLanguageDependent: Bool let type: SubmoduleType } diff --git a/Stepic/Sources/Modules/Home/HomeViewController.swift b/Stepic/Sources/Modules/Home/HomeViewController.swift index bd5c5e320b..d8b60fdee8 100644 --- a/Stepic/Sources/Modules/Home/HomeViewController.swift +++ b/Stepic/Sources/Modules/Home/HomeViewController.swift @@ -1,4 +1,5 @@ import PromiseKit +import SnapKit import UIKit protocol HomeViewControllerProtocol: BaseExploreViewControllerProtocol { @@ -10,6 +11,10 @@ protocol HomeViewControllerProtocol: BaseExploreViewControllerProtocol { } final class HomeViewController: BaseExploreViewController { + enum Appearance { + static let continueCourseHeight: CGFloat = 72 + } + enum Animation { static let startRefreshDelay: TimeInterval = 1.0 static let modulesRefreshDelay: TimeInterval = 0.3 @@ -18,7 +23,6 @@ final class HomeViewController: BaseExploreViewController { fileprivate static let submodulesOrder: [Home.Submodule] = [ .stories, .streakActivity, - .continueCourse, .enrolledCourses, .visitedCourses, .popularCourses @@ -167,14 +171,22 @@ final class HomeViewController: BaseExploreViewController { output: self.interactor as? ContinueCourseOutputProtocol ) let continueCourseViewController = continueCourseAssembly.makeModule() + self.registerSubmodule( .init( viewController: continueCourseViewController, view: continueCourseViewController.view, + isArrangeable: false, isLanguageDependent: false, type: Home.Submodule.continueCourse ) ) + + continueCourseViewController.view.snp.makeConstraints { make in + make.leading.trailing.equalToSuperview() + make.bottom.equalTo(self.view.safeAreaLayoutGuide) + make.height.equalTo(Appearance.continueCourseHeight) + } } // MARK: - Fullscreen displaying @@ -479,10 +491,7 @@ extension HomeViewController: HomeViewControllerProtocol { func displayModuleErrorState(viewModel: Home.CourseListStateUpdate.ViewModel) { switch viewModel.module { case .continueCourse: - switch viewModel.result { - default: - self.refreshContinueCourse(state: .hidden) - } + self.refreshContinueCourse(state: .hidden) case .enrolledCourses: switch viewModel.result { case .empty: From 8443c659e7a758397856e38f085ea989e84fdfea Mon Sep 17 00:00:00 2001 From: Ivan Magda Date: Tue, 9 Feb 2021 12:33:42 +0300 Subject: [PATCH 2/5] Update appearance --- Stepic.xcodeproj/project.pbxproj | 30 +-- .../Continue Learning/Contents.json | 6 + .../Contents.json | 15 ++ .../continue_learning_arrow_right.pdf | Bin 0 -> 1288 bytes .../Contents.json | 15 ++ .../continue_learning_gradient.pdf | Bin 0 -> 2053 bytes .../Course_list_placeholder/Contents.json | 6 +- .../Contents.json | 20 +- .../Contents.json | 20 +- .../Contents.json | 20 +- .../Contents.json | 10 +- .../Contents.json | 10 +- .../Contents.json | 10 +- .../CodeEditorPreviewView.swift | 2 +- ...rsonalDeadlineModeCollectionViewCell.swift | 2 +- .../QuizViewController.swift | 2 +- .../StringQuiz/StringQuizViewController.swift | 2 +- .../StoryPartViews/TextStoryView.swift | 2 +- .../StepikVideoPlayerViewController.swift | 6 +- .../Legacy/Extensions/UIViewExtensions.swift | 31 --- Stepic/Legacy/Views/AvatarImageView.swift | 4 +- ...ersonalDeadlinesSuggestionWidgetView.swift | 2 +- Stepic/Legacy/Views/StepikButton.swift | 2 +- .../Extensions/UIKit/UIViewExtensions.swift | 57 ++++ .../Views/Widget/CourseWidgetStatsView.swift | 6 +- .../ContinueCourseViewController.swift | 11 +- .../{ => Views}/ContinueCourseView.swift | 44 +++- .../Views/ContinueLastStepView.swift | 196 ++++++++++++++ .../NewFreeAnswerQuizView.swift | 4 +- .../NewStringQuiz/NewStringQuizView.swift | 4 +- .../ContinueActionButton.swift | 0 .../ContinueLastStepView.swift | 248 ------------------ 32 files changed, 399 insertions(+), 388 deletions(-) create mode 100644 Stepic/Images.xcassets/Continue Learning/Contents.json create mode 100644 Stepic/Images.xcassets/Continue Learning/continue_learning_arrow_right.imageset/Contents.json create mode 100644 Stepic/Images.xcassets/Continue Learning/continue_learning_arrow_right.imageset/continue_learning_arrow_right.pdf create mode 100644 Stepic/Images.xcassets/Continue Learning/continue_learning_gradient.imageset/Contents.json create mode 100644 Stepic/Images.xcassets/Continue Learning/continue_learning_gradient.imageset/continue_learning_gradient.pdf delete mode 100644 Stepic/Legacy/Extensions/UIViewExtensions.swift create mode 100644 Stepic/Sources/Extensions/UIKit/UIViewExtensions.swift rename Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/{ => Views}/ContinueCourseView.swift (62%) create mode 100644 Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueLastStepView.swift rename Stepic/Sources/Views/{ContinueLastStepView => }/ContinueActionButton.swift (100%) delete mode 100644 Stepic/Sources/Views/ContinueLastStepView/ContinueLastStepView.swift diff --git a/Stepic.xcodeproj/project.pbxproj b/Stepic.xcodeproj/project.pbxproj index d5ced1fa92..1e76fc5074 100644 --- a/Stepic.xcodeproj/project.pbxproj +++ b/Stepic.xcodeproj/project.pbxproj @@ -234,7 +234,6 @@ 089877AD214047EE0065DFA2 /* Numbers+Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089877A8214047EC0065DFA2 /* Numbers+Random.swift */; }; 089877B0214047EE0065DFA2 /* UserDefaults+StorageServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089877AB214047ED0065DFA2 /* UserDefaults+StorageServiceProtocol.swift */; }; 089877B221404CF10065DFA2 /* StringStorageServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089877B121404CF00065DFA2 /* StringStorageServiceProtocol.swift */; }; - 089A0DA71BE9FFCE004AF4EB /* UIViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089A0DA61BE9FFCE004AF4EB /* UIViewExtensions.swift */; }; 08A0218E1D675B4700915679 /* SectionNavigationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A0218D1D675B4700915679 /* SectionNavigationDelegate.swift */; }; 08A1256F1BDE8E460066B2B2 /* Sorter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A1256E1BDE8E460066B2B2 /* Sorter.swift */; }; 08A125791BDEBCC90066B2B2 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 08A1257B1BDEBCC90066B2B2 /* Localizable.strings */; }; @@ -938,6 +937,7 @@ 2CF6E63225ADB92B00B5A703 /* StepicWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 2CF6E62525ADB92A00B5A703 /* StepicWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 2CF7E3342417C1A700B9188E /* UseCellularDataForDownloadsStorageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CF7E3332417C1A700B9188E /* UseCellularDataForDownloadsStorageManager.swift */; }; 2CF9BD3C2538508800C2AFD2 /* PromiseKit+Retry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CF9BD3B2538508800C2AFD2 /* PromiseKit+Retry.swift */; }; + 2CFA48A025D1828C00BECD32 /* UIViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFA489F25D1828C00BECD32 /* UIViewExtensions.swift */; }; 2CFC5ABC228ADFC400B5248A /* StepsPersistenceService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFC5ABB228ADFC400B5248A /* StepsPersistenceService.swift */; }; 2CFDB1241F559F9A00B8035C /* AvatarImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFDB1231F559F9A00B8035C /* AvatarImageView.swift */; }; 2CFE6A84255AA6DE00A43952 /* CatalogBlockContentItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFE6A83255AA6DE00A43952 /* CatalogBlockContentItemTests.swift */; }; @@ -1799,7 +1799,6 @@ 089877AB214047ED0065DFA2 /* UserDefaults+StorageServiceProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+StorageServiceProtocol.swift"; sourceTree = ""; }; 089877B121404CF00065DFA2 /* StringStorageServiceProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringStorageServiceProtocol.swift; sourceTree = ""; }; 089877B521407AB50065DFA2 /* Model_profile_staff_v24.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model_profile_staff_v24.xcdatamodel; sourceTree = ""; }; - 089A0DA61BE9FFCE004AF4EB /* UIViewExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewExtensions.swift; sourceTree = ""; }; 08A0218D1D675B4700915679 /* SectionNavigationDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionNavigationDelegate.swift; sourceTree = ""; }; 08A1256E1BDE8E460066B2B2 /* Sorter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sorter.swift; sourceTree = ""; }; 08A1257A1BDEBCC90066B2B2 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; @@ -2561,6 +2560,7 @@ 2CF6E62F25ADB92B00B5A703 /* Info-Production.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-Production.plist"; sourceTree = ""; }; 2CF7E3332417C1A700B9188E /* UseCellularDataForDownloadsStorageManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UseCellularDataForDownloadsStorageManager.swift; sourceTree = ""; }; 2CF9BD3B2538508800C2AFD2 /* PromiseKit+Retry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PromiseKit+Retry.swift"; sourceTree = ""; }; + 2CFA489F25D1828C00BECD32 /* UIViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewExtensions.swift; sourceTree = ""; }; 2CFC5ABB228ADFC400B5248A /* StepsPersistenceService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepsPersistenceService.swift; sourceTree = ""; }; 2CFDA8181FBB3CFD0098A441 /* Model_sections_with_course_id_v21.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model_sections_with_course_id_v21.xcdatamodel; sourceTree = ""; }; 2CFDB1231F559F9A00B8035C /* AvatarImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarImageView.swift; sourceTree = ""; }; @@ -3436,6 +3436,7 @@ 2C5240D22399257B000DDA13 /* UIStatusBarStyle+DarkContent.swift */, 62E9845643D7541307856023 /* UITableViewExtensions.swift */, 2C9776E924222AED0097AEFC /* UIView+TraitCollection.swift */, + 2CFA489F25D1828C00BECD32 /* UIViewExtensions.swift */, ); path = UIKit; sourceTree = ""; @@ -3788,6 +3789,15 @@ path = SocialProfile; sourceTree = ""; }; + 2C64388D25D17F480045D139 /* Views */ = { + isa = PBXGroup; + children = ( + 62E98021576A447517397409 /* ContinueCourseView.swift */, + 62E98FC21463AD1C43A467BB /* ContinueLastStepView.swift */, + ); + path = Views; + sourceTree = ""; + }; 2C6B2F6121AE659600DEB6AC /* Extensions */ = { isa = PBXGroup; children = ( @@ -3803,7 +3813,6 @@ 08CA59E91BBD3D55008DC44D /* UILabelExtensions.swift */, 62E98D9D45E6DA6099AF1C0B /* UITableView+TableHeaderLayout.swift */, 62E982BB5246C2BBC5E88D06 /* UIView+FromNib.swift */, - 089A0DA61BE9FFCE004AF4EB /* UIViewExtensions.swift */, 089877AB214047ED0065DFA2 /* UserDefaults+StorageServiceProtocol.swift */, ); path = Extensions; @@ -5642,15 +5651,6 @@ path = AchievementBadgeView; sourceTree = ""; }; - 2CFF8FFE242A22DF00FD7311 /* ContinueLastStepView */ = { - isa = PBXGroup; - children = ( - 62E9807E3506DAF40179593F /* ContinueActionButton.swift */, - 62E98FC21463AD1C43A467BB /* ContinueLastStepView.swift */, - ); - path = ContinueLastStepView; - sourceTree = ""; - }; 2CFF8FFF242A22EB00FD7311 /* LoadingPaginationView */ = { isa = PBXGroup; children = ( @@ -7082,6 +7082,7 @@ isa = PBXGroup; children = ( 62E98EDC02AF16EA5BAA748A /* BounceButton.swift */, + 62E9807E3506DAF40179593F /* ContinueActionButton.swift */, 62E9888DC7DFE8CFE5C220B6 /* CourseCoverImageView.swift */, 2C381BEE25505EC90084AD90 /* CourseListFilterBarButtonItem.swift */, 62E9856B0B8FB44A5492EF67 /* CourseRatingView.swift */, @@ -7102,7 +7103,6 @@ 62E986AC65867C28C93D475E /* TableInputTextField.swift */, 2C06E09A2241045800AF4DA2 /* TableInputTextView.swift */, 62E988C4DC97F14EAC09BC41 /* TabSegmentedControlView.swift */, - 2CFF8FFE242A22DF00FD7311 /* ContinueLastStepView */, 2C11C9F024EFC80400A4647B /* Layouts */, 62E989892C62C2BC2B1A3741 /* TabBar */, ); @@ -7716,9 +7716,9 @@ 62E98B7A07FDA68E6CA47ECB /* ContinueCourseOutputProtocol.swift */, 62E98A6EFA74ACC7FF243E12 /* ContinueCoursePresenter.swift */, 62E9818650597467F6A1D814 /* ContinueCourseProvider.swift */, - 62E98021576A447517397409 /* ContinueCourseView.swift */, 62E98EC2E16550A310CE6CEF /* ContinueCourseViewController.swift */, 62E984C48E60011A6C45B243 /* ContinueCourseViewModel.swift */, + 2C64388D25D17F480045D139 /* Views */, ); path = ContinueCourse; sourceTree = ""; @@ -9028,7 +9028,6 @@ 08421BCB21764FC400E8A81B /* ActiveSplitTestsContainer.swift in Sources */, 2C85C6A522D38A3800FDBAFE /* VotesNetworkService.swift in Sources */, 2CDC9EFA24E4FD0D00916BAE /* CourseInfoTryForFreeButton.swift in Sources */, - 089A0DA71BE9FFCE004AF4EB /* UIViewExtensions.swift in Sources */, 2CD04084250F67D0004D284F /* ProcessedContent.swift in Sources */, 0828FF6E1BC5D177000AFEA7 /* Section+CoreDataProperties.swift in Sources */, 2CB9E8D01F839C8E0004E17F /* NotificationsTableViewCell.swift in Sources */, @@ -9967,6 +9966,7 @@ 62E987CF11C3A59E26F3E888 /* StepPresenter.swift in Sources */, 62E984897A5BBA262FC4F5E2 /* StepProvider.swift in Sources */, 62E987EA4263A9FE06376867 /* StepView.swift in Sources */, + 2CFA48A025D1828C00BECD32 /* UIViewExtensions.swift in Sources */, 62E9888DF0D85074AFE0092C /* StepViewController.swift in Sources */, 62E986D61FF5B6B4692F3D7F /* StepViewModel.swift in Sources */, 62E98BFC923309B4590E58C3 /* StepInputProtocol.swift in Sources */, diff --git a/Stepic/Images.xcassets/Continue Learning/Contents.json b/Stepic/Images.xcassets/Continue Learning/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/Stepic/Images.xcassets/Continue Learning/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stepic/Images.xcassets/Continue Learning/continue_learning_arrow_right.imageset/Contents.json b/Stepic/Images.xcassets/Continue Learning/continue_learning_arrow_right.imageset/Contents.json new file mode 100644 index 0000000000..b81355b517 --- /dev/null +++ b/Stepic/Images.xcassets/Continue Learning/continue_learning_arrow_right.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "continue_learning_arrow_right.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Stepic/Images.xcassets/Continue Learning/continue_learning_arrow_right.imageset/continue_learning_arrow_right.pdf b/Stepic/Images.xcassets/Continue Learning/continue_learning_arrow_right.imageset/continue_learning_arrow_right.pdf new file mode 100644 index 0000000000000000000000000000000000000000..884ba2aba60e1fd2771e0e654e42eba7a8edee35 GIT binary patch literal 1288 zcmZvcOK;Oa5PsnAt5lcaBjwFo3BsZm(iC2F4TD>6gSSHu@HE0 z0lj&FR8T7TiEhO__rh5uRmrt5#swG=a&3%PimVN)f_o=~rUEc@BvUN{NXeDg&RPm4 zUq!`z2%19Jj$Q~VikgDRUXYZn3I<0nl%{hYttKxy5rP<`at_9PCoETHsywM;?!lHl zj;)iF&mnSZP444Jw^K?94Oxy?UxnFfa`wP#_JyUXJ&xosziCttb8WTHQ>`mO z?bq^XZr0R&tjS|qt^qE}mKc~Lnyg?#c{PgrHtVBmO1gV3i03vwFaeGob~!!48lwylJ{}aXm!Qr_QJm?F$SHq^R(SNs4 z2$xacmYZ#CS?b>VIhEtQYj*Jh&Nr)zLs5!l)AW!M-WRyI+x(3b{r;a-w*5XG`de6b JcJ}u3?LSHL7(D<0 literal 0 HcmV?d00001 diff --git a/Stepic/Images.xcassets/Continue Learning/continue_learning_gradient.imageset/Contents.json b/Stepic/Images.xcassets/Continue Learning/continue_learning_gradient.imageset/Contents.json new file mode 100644 index 0000000000..c23d1304c6 --- /dev/null +++ b/Stepic/Images.xcassets/Continue Learning/continue_learning_gradient.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "continue_learning_gradient.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Stepic/Images.xcassets/Continue Learning/continue_learning_gradient.imageset/continue_learning_gradient.pdf b/Stepic/Images.xcassets/Continue Learning/continue_learning_gradient.imageset/continue_learning_gradient.pdf new file mode 100644 index 0000000000000000000000000000000000000000..dea47dda74506db2fdb78772967513a5b5b09836 GIT binary patch literal 2053 zcma)7OK;mS48H4E@KT^XB(~%?Py|?$b{Mu|S+d)(gKDdZhs4TkXUJCczmJkF*-pK+ zIwaI5lH%)=FP4kD37tlSP;fy${2&0=*Wi2-#irf@LvV!OPiO7@-mx|sxAfJlPAE766!@P0C7sUYR700CUzIQ zUc#DPc06=pZEARG;DbDj2m|Hn_y7|XOBDMa?2o(dR-R=e-jxCBv&avq(Qo4VuRcve zH#YiB3u4bBW?s5I!KpQy4&L=)Yqsk=jA4EAEqNpb`9elWsw&Wslrhg1 zE3~L0>ARwkHS(h!U|ar;Xln1=i!A3iQjyk%VujI^jH{KXLZkUe;% zYMKF;yhIG57V5QDK}`-S+K;*BvPxlPUI3i$Bihn7UNb)%Q6bXv6c0Q#YI9!mo!qph zOO8fkL7YnQNL)xr^ZC+45e9)50K)B}E@%m``^^eGWQ|%qD8Nz-AWCCw(-87Qtj!q| zW6nb;49{<*k=Gg4CC_)FB&uv57TCSZO76uKT;|zTQ#xlQWeut Void)? - - init(frame: CGRect = .zero, appearance: Appearance = Appearance()) { - self.appearance = appearance - super.init(frame: frame) - - self.setupView() - self.addSubviews() - self.makeConstraints() - } - - @available(*, unavailable) - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - self.performBlockIfAppearanceChanged(from: previousTraitCollection) { - self.updateViewColor() - } - } - - private func updateViewColor() { - self.progressView.progressTintColor = self.appearance.progressFillColor - self.overlayView.backgroundColor = self.isDarkInterfaceStyle - ? self.appearance.darkModeBackgroundOverlayViewColor - : self.appearance.lightModeBackgroundOverlayViewColor - } - - @objc - private func continueButtonClicked() { - self.onContinueButtonClick?() - } -} - -extension ContinueLastStepView: ProgrammaticallyInitializableViewProtocol { - func setupView() { - self.updateViewColor() - } - - func addSubviews() { - self.labelsStackView.addArrangedSubview(self.courseNameLabel) - self.labelsStackView.addArrangedSubview(self.progressLabel) - - self.infoStackView.addArrangedSubview(self.coverImageView) - self.infoStackView.addArrangedSubview(self.labelsStackView) - - self.continueButtonBlock.addSubview(self.continueButton) - self.contentStackView.addArrangedSubview(self.continueButtonBlock) - self.contentStackView.addArrangedSubview(self.infoStackView) - - self.addSubview(self.backgroundImageView) - self.addSubview(self.overlayView) - - self.overlayView.addSubview(self.contentStackView) - self.overlayView.addSubview(self.progressView) - } - - func makeConstraints() { - self.backgroundImageView.translatesAutoresizingMaskIntoConstraints = false - self.backgroundImageView.snp.makeConstraints { make in - make.center.equalTo(self.overlayView) - make.size.equalTo(self.overlayView) - } - - self.overlayView.translatesAutoresizingMaskIntoConstraints = false - self.overlayView.snp.makeConstraints { make in - make.leading.equalToSuperview().offset(self.appearance.mainInsets.left) - make.trailing.equalToSuperview().offset(-self.appearance.mainInsets.right) - make.top.equalToSuperview().offset(self.appearance.mainInsets.top) - make.bottom.equalToSuperview().offset(-self.appearance.mainInsets.bottom) - } - - self.contentStackView.translatesAutoresizingMaskIntoConstraints = false - self.contentStackView.snp.makeConstraints { make in - make.leading.equalToSuperview().offset(self.appearance.contentInsets.left) - make.trailing.equalToSuperview().offset(-self.appearance.contentInsets.right) - make.top.equalToSuperview().offset(self.appearance.contentInsets.top) - make.bottom.equalToSuperview().offset(-self.appearance.contentInsets.bottom) - } - - self.progressView.translatesAutoresizingMaskIntoConstraints = false - self.progressView.snp.makeConstraints { make in - make.leading.trailing.bottom.equalToSuperview() - make.height.equalTo(self.appearance.progressHeight) - } - - self.coverImageView.translatesAutoresizingMaskIntoConstraints = false - self.coverImageView.snp.makeConstraints { make in - make.size.equalTo(self.appearance.coverSize) - } - - self.continueButtonBlock.translatesAutoresizingMaskIntoConstraints = false - self.continueButtonBlock.snp.makeConstraints { make in - make.height.equalTo(self.appearance.continueButtonHeight) - } - - self.continueButton.translatesAutoresizingMaskIntoConstraints = false - self.continueButton.snp.makeConstraints { make in - make.center.equalToSuperview() - make.top.bottom.equalToSuperview() - make.width.equalTo(self.snp.width).multipliedBy(self.appearance.continueButtonWidthRatio) - } - } -} From e6610b185875168803b22ddce7f7fe507f5d0aa3 Mon Sep 17 00:00:00 2001 From: Ivan Magda Date: Tue, 9 Feb 2021 15:13:56 +0300 Subject: [PATCH 3/5] Fix contentInsets --- Stepic/Sources/Modules/BaseExplore/BaseExploreView.swift | 9 +++++++++ Stepic/Sources/Modules/Home/HomeViewController.swift | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/Stepic/Sources/Modules/BaseExplore/BaseExploreView.swift b/Stepic/Sources/Modules/BaseExplore/BaseExploreView.swift index cfe678e311..d748dfff4e 100644 --- a/Stepic/Sources/Modules/BaseExplore/BaseExploreView.swift +++ b/Stepic/Sources/Modules/BaseExplore/BaseExploreView.swift @@ -77,6 +77,15 @@ extension BaseExploreView: ProgrammaticallyInitializableViewProtocol { } extension BaseExploreView: ScrollableStackViewDelegate { + var contentInsets: UIEdgeInsets { + get { + self.scrollableStackView.contentInsets + } + set { + self.scrollableStackView.contentInsets = newValue + } + } + func scrollableStackViewRefreshControlDidRefresh(_ scrollableStackView: ScrollableStackView) { self.delegate?.refreshControlDidRefresh() } diff --git a/Stepic/Sources/Modules/Home/HomeViewController.swift b/Stepic/Sources/Modules/Home/HomeViewController.swift index d8b60fdee8..b2b0ec3e86 100644 --- a/Stepic/Sources/Modules/Home/HomeViewController.swift +++ b/Stepic/Sources/Modules/Home/HomeViewController.swift @@ -159,10 +159,19 @@ final class HomeViewController: BaseExploreViewController { } private func refreshContinueCourse(state: ContinueCourseState) { + var contentInsets = self.exploreView?.contentInsets ?? .zero + if let submodule = self.getSubmodule(type: Home.Submodule.continueCourse) { self.removeSubmodule(submodule) } + defer { + contentInsets.bottom = state == .shown + ? (Appearance.continueCourseHeight + LayoutInsets.default.bottom) + : 0 + self.exploreView?.contentInsets = contentInsets + } + guard case .shown = state else { return } From 472a74bc83832c1d676e06d3026197abd5c3573a Mon Sep 17 00:00:00 2001 From: Ivan Magda Date: Tue, 9 Feb 2021 15:36:37 +0300 Subject: [PATCH 4/5] Update loading skeleton --- .../ContinueCourseSkeletonView.swift | 59 +++++++++++++++---- .../Views/ContinueCourseView.swift | 6 +- .../Views/ContinueLastStepView.swift | 20 +++++-- 3 files changed, 64 insertions(+), 21 deletions(-) diff --git a/Stepic/Legacy/Views/Skeleton/Views/CourseList/ContinueCourseSkeletonView.swift b/Stepic/Legacy/Views/Skeleton/Views/CourseList/ContinueCourseSkeletonView.swift index a055464cdb..5843ec3923 100644 --- a/Stepic/Legacy/Views/Skeleton/Views/CourseList/ContinueCourseSkeletonView.swift +++ b/Stepic/Legacy/Views/Skeleton/Views/CourseList/ContinueCourseSkeletonView.swift @@ -11,15 +11,26 @@ import UIKit extension ContinueCourseSkeletonView { struct Appearance { - let mainInsets = UIEdgeInsets(top: 20, left: 20, bottom: 0, right: 20) - let cornerRadius: CGFloat = 8.0 + let labelsCornerRadius: CGFloat = 5 + let defaultInsets = LayoutInsets.default + + let coverCornerRadius: CGFloat = 8 + let coverSize = CGSize(width: 40, height: 40) + + let courseLabelInsets = LayoutInsets(left: 8, right: 8) + let courseLabelWidthRatio: CGFloat = 0.7 + + let statsViewHeight: CGFloat = 17 + let statsViewWidthRatio: CGFloat = 0.4 } } final class ContinueCourseSkeletonView: UIView { let appearance: Appearance - private lazy var largeView = UIView() + private lazy var courseCoverView = UIView() + private lazy var courseLabelView = UIView() + private lazy var courseStatsView = UIView() init(frame: CGRect = .zero, appearance: Appearance = Appearance()) { self.appearance = appearance @@ -39,21 +50,45 @@ extension ContinueCourseSkeletonView: ProgrammaticallyInitializableViewProtocol func setupView() { self.backgroundColor = .clear - self.largeView.clipsToBounds = true - self.largeView.layer.cornerRadius = self.appearance.cornerRadius + self.courseCoverView.clipsToBounds = true + self.courseCoverView.layer.cornerRadius = self.appearance.coverCornerRadius + + self.courseLabelView.clipsToBounds = true + self.courseLabelView.layer.cornerRadius = self.appearance.labelsCornerRadius + + self.courseStatsView.clipsToBounds = true + self.courseStatsView.layer.cornerRadius = self.appearance.labelsCornerRadius } func addSubviews() { - self.addSubview(self.largeView) + self.addSubviews([self.courseCoverView, self.courseLabelView, self.courseStatsView]) } func makeConstraints() { - self.largeView.translatesAutoresizingMaskIntoConstraints = false - self.largeView.snp.makeConstraints { make in - make.top.equalToSuperview().offset(self.appearance.mainInsets.top) - make.leading.equalToSuperview().offset(self.appearance.mainInsets.left) - make.trailing.equalToSuperview().offset(-self.appearance.mainInsets.right) - make.bottom.equalToSuperview().offset(self.appearance.mainInsets.bottom) + self.courseCoverView.translatesAutoresizingMaskIntoConstraints = false + self.courseCoverView.snp.makeConstraints { make in + make.leading + .equalTo(self.safeAreaLayoutGuide.snp.leading) + .offset(self.appearance.defaultInsets.left) + make.centerY.equalToSuperview() + make.size.equalTo(self.appearance.coverSize) + } + + self.courseLabelView.translatesAutoresizingMaskIntoConstraints = false + self.courseLabelView.snp.makeConstraints { make in + make.top.equalTo(self.courseCoverView.snp.top) + make.leading + .equalTo(self.courseCoverView.snp.trailing) + .offset(self.appearance.courseLabelInsets.left) + make.width.equalToSuperview().multipliedBy(self.appearance.courseLabelWidthRatio) + } + + self.courseStatsView.translatesAutoresizingMaskIntoConstraints = false + self.courseStatsView.snp.makeConstraints { make in + make.leading.equalTo(self.courseLabelView.snp.leading) + make.bottom.equalTo(self.courseCoverView.snp.bottom) + make.height.equalTo(self.appearance.statsViewHeight) + make.width.equalToSuperview().multipliedBy(self.appearance.statsViewWidthRatio) } } } diff --git a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueCourseView.swift b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueCourseView.swift index 74d864ad5d..09ddb43aae 100644 --- a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueCourseView.swift +++ b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueCourseView.swift @@ -57,13 +57,13 @@ final class ContinueCourseView: UIView { } func showLoading() { - self.skeleton.viewBuilder = { - ContinueCourseSkeletonView() - } + self.lastStepView.setContentHidden(true) + self.skeleton.viewBuilder = { ContinueCourseSkeletonView() } self.skeleton.show() } func hideLoading() { + self.lastStepView.setContentHidden(false) self.skeleton.hide() } diff --git a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueLastStepView.swift b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueLastStepView.swift index 17c7325c8b..995c21185a 100644 --- a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueLastStepView.swift +++ b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueLastStepView.swift @@ -92,14 +92,18 @@ final class ContinueLastStepView: UIControl { var tooltipAnchorView: UIView { self.rightDetailImageView } + private var contentViews: [UIView] { + [ + self.coverImageView, + self.courseNameLabel, + self.statsView, + self.rightDetailImageView + ] + } + override var isHighlighted: Bool { didSet { - [ - self.coverImageView, - self.courseNameLabel, - self.statsView, - self.rightDetailImageView - ].forEach { $0.alpha = self.isHighlighted ? 0.5 : 1.0 } + self.contentViews.forEach { $0.alpha = self.isHighlighted ? 0.5 : 1.0 } } } @@ -125,6 +129,10 @@ final class ContinueLastStepView: UIControl { } } + func setContentHidden(_ isHidden: Bool) { + self.contentViews.forEach { $0.isHidden = isHidden } + } + private func updateBackground() { self.backgroundColor = self.appearance.backgroundColor self.backgroundImageView.isHidden = self.isDarkInterfaceStyle From 9f6f253d87746f08fdda642d0d5c3f345b2aae59 Mon Sep 17 00:00:00 2001 From: Ivan Magda Date: Tue, 9 Feb 2021 17:56:47 +0300 Subject: [PATCH 5/5] Handle empty & error states --- Stepic.xcodeproj/project.pbxproj | 8 ++ .../plus.imageset/Contents.json | 15 +++ Stepic/Images.xcassets/plus.imageset/plus.pdf | Bin 0 -> 4458 bytes .../ContinueCourseSkeletonView.swift | 2 + .../ContinueCourseAssembly.swift | 8 +- .../ContinueCourseDataFlow.swift | 4 +- .../ContinueCourseInteractor.swift | 15 ++- .../ContinueCourseOutputProtocol.swift | 1 - .../ContinueCoursePresenter.swift | 18 ++-- .../ContinueCourseProvider.swift | 59 +++++----- .../ContinueCourseViewController.swift | 69 ++++++++---- .../Views/ContinueCourseBackgroundView.swift | 63 +++++++++++ .../Views/ContinueCourseEmptyView.swift | 101 ++++++++++++++++++ .../Views/ContinueCourseView.swift | 59 ++++++++-- .../Views/ContinueLastStepView.swift | 55 +--------- .../Sources/Modules/Home/HomeInteractor.swift | 11 +- .../Modules/Home/HomeViewController.swift | 3 +- .../Services/ApplicationShortcutService.swift | 4 +- Stepic/en.lproj/Localizable.strings | 2 + Stepic/ru.lproj/Localizable.strings | 2 + 20 files changed, 355 insertions(+), 144 deletions(-) create mode 100644 Stepic/Images.xcassets/plus.imageset/Contents.json create mode 100644 Stepic/Images.xcassets/plus.imageset/plus.pdf create mode 100644 Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueCourseBackgroundView.swift create mode 100644 Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueCourseEmptyView.swift diff --git a/Stepic.xcodeproj/project.pbxproj b/Stepic.xcodeproj/project.pbxproj index 1e76fc5074..2b2874cd6b 100644 --- a/Stepic.xcodeproj/project.pbxproj +++ b/Stepic.xcodeproj/project.pbxproj @@ -518,6 +518,8 @@ 2C4436B321356D960084489C /* CourseSubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088FD8161FB242B3008A2953 /* CourseSubscriber.swift */; }; 2C453398204D46E90061342A /* PinsMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C453397204D46E90061342A /* PinsMap.swift */; }; 2C45339A204D5DEE0061342A /* PinsMapSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C453399204D5DEE0061342A /* PinsMapSpec.swift */; }; + 2C456C3425D2C47100435A86 /* ContinueCourseEmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C456C3325D2C47100435A86 /* ContinueCourseEmptyView.swift */; }; + 2C456C3E25D2C52F00435A86 /* ContinueCourseBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C456C3D25D2C52F00435A86 /* ContinueCourseBackgroundView.swift */; }; 2C45C6EF254160A100AF24BF /* HTMLExtractorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C45C6EE254160A100AF24BF /* HTMLExtractorTests.swift */; }; 2C47A164206284B1003E87EC /* NotificationRequestAlertContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C47A163206284B1003E87EC /* NotificationRequestAlertContext.swift */; }; 2C48BA0725727822009103B2 /* AuthorsCourseListCollectionViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C48BA0625727822009103B2 /* AuthorsCourseListCollectionViewDataSource.swift */; }; @@ -2122,6 +2124,8 @@ 2C434D4425B7367500854D6F /* WidgetContentProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetContentProvider.swift; sourceTree = ""; }; 2C453397204D46E90061342A /* PinsMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinsMap.swift; sourceTree = ""; }; 2C453399204D5DEE0061342A /* PinsMapSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinsMapSpec.swift; sourceTree = ""; }; + 2C456C3325D2C47100435A86 /* ContinueCourseEmptyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContinueCourseEmptyView.swift; sourceTree = ""; }; + 2C456C3D25D2C52F00435A86 /* ContinueCourseBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContinueCourseBackgroundView.swift; sourceTree = ""; }; 2C45C6EE254160A100AF24BF /* HTMLExtractorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTMLExtractorTests.swift; sourceTree = ""; }; 2C46417A24CFD23400FB6696 /* Model_lessons_demo_access_v58.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model_lessons_demo_access_v58.xcdatamodel; sourceTree = ""; }; 2C47A163206284B1003E87EC /* NotificationRequestAlertContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationRequestAlertContext.swift; sourceTree = ""; }; @@ -3792,6 +3796,8 @@ 2C64388D25D17F480045D139 /* Views */ = { isa = PBXGroup; children = ( + 2C456C3D25D2C52F00435A86 /* ContinueCourseBackgroundView.swift */, + 2C456C3325D2C47100435A86 /* ContinueCourseEmptyView.swift */, 62E98021576A447517397409 /* ContinueCourseView.swift */, 62E98FC21463AD1C43A467BB /* ContinueLastStepView.swift */, ); @@ -9211,6 +9217,7 @@ 0885F84E1BA837E200F2A188 /* ApiDataDownloader.swift in Sources */, 2C5E90842333DF2100288BE3 /* CodeLanguageSuggestionsService.swift in Sources */, 2CB9E8C41F7AA5CD0004E17F /* NotificationsPresenter.swift in Sources */, + 2C456C3425D2C47100435A86 /* ContinueCourseEmptyView.swift in Sources */, 2C8BCC222486540F00DFB009 /* ShowAllButton.swift in Sources */, 08F485A21C57987C000165AA /* NumberQuizViewController.swift in Sources */, 2C5DF1431FED2758003B1177 /* CardStepDelegate.swift in Sources */, @@ -9636,6 +9643,7 @@ 62E980B234CAE5A61B9E546C /* CourseInfoTabSyllabusView.swift in Sources */, 2C698C9524CEA90100979661 /* NewProfileCoverView.swift in Sources */, 62E9870EF451D397B27AB075 /* CourseInfoTabSyllabusCellStatsView.swift in Sources */, + 2C456C3E25D2C52F00435A86 /* ContinueCourseBackgroundView.swift in Sources */, 62E9893B98E774DA8141AD0F /* CourseInfoTabSyllabusCellView.swift in Sources */, 2C2F6E6024DD3E69000E8844 /* StepikAnalyticsEvents.swift in Sources */, 62E98C92B0827BEB4E28A8D9 /* CourseInfoTabSyllabusTableViewCell.swift in Sources */, diff --git a/Stepic/Images.xcassets/plus.imageset/Contents.json b/Stepic/Images.xcassets/plus.imageset/Contents.json new file mode 100644 index 0000000000..0babeff575 --- /dev/null +++ b/Stepic/Images.xcassets/plus.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "plus.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Stepic/Images.xcassets/plus.imageset/plus.pdf b/Stepic/Images.xcassets/plus.imageset/plus.pdf new file mode 100644 index 0000000000000000000000000000000000000000..28c8c8d8db7b421cd71f87c9fc20a847eff57a0f GIT binary patch literal 4458 zcmeHKU5Fc16jmx)uXRfmTF^?bZew=5O=jZE zWOp`Fg{lwYgDkkvhef*BRj^nD@o6jeK?T85)K(C+D)dR(`c$eGt>?~9lHI++n`Jcy2hvhodO)@ud^jPy};WpMD;tQkd+aTG^VxQ7{z{#+VGN6^j~h zu1FTmQCU|g%4Cpf8C0qwd+ESJUfaNLeC_Je;k#b_YuiQn!p6mwV{d%<(51guxY$?6 z7FHHAU!?3aTV8p4?Cj>r?VIhvBP%~Y`qH60PF~-={9SM9;mZ%~IC9^WsilE(&(6{F z@h8U`RZ_k%_~R`fZQZ~1*Hh2-@9aHy@4`E$XI}3q-;aLy=e@;QZTZkA6XnIX-hbrU zZ&!}y3R9miZ8&#gZ2$4gp$}F+{F?vXHuv3`=-;yU#c$t4pIzLyZPOWPc_^~F{oP-< z!{+(Nub=uUYiv4eiaUBVX%iz77SU@|dZ zqnM0KWr_oscjAsq2GuNh)aNBn6;&G4ZTt+FI2?eOFl!7E2+Rhbq>2AR?AgcPW$*A1DPnBh@q>x)(F z4_B)%lRZnuRKTYy+wfN?`t>N1RDpDd|{j`=Vj4f7oCo3X4~k^s11 zEgMp8MwQL5Zj}4`okUp}R!h~j93!SLmlQaG=L9an4MZZM5aMD%j_YsBInb8VEjlY% zQbjL2(HJqNM@5T3+n^=H;W*-&2T&3fqqu~9TTB41*nzdCEy4j!>ujP3n5?sLJPhDE zn+UzHx5eT&`o%e*!GG2=B(*{fXS)~G28EIt83kRpFl*eMm{F~yV}`v$rfErr<#ZU2 ziYO2m+B=K_gS4z_L$+F)(DlkwH3h&$Ew&JGP}hO1r=@B`GWro|@#mXb__Z)c-Uje7 zcXomgILT8>f>aWY`^9-Xh^^g8^8@&SXI)&$!MO-*Rpg*eXL>7V&jPkxp7cVD8XGBl(Lg{wN{0yuwAWa zX3|avC3rQ^BwRBy34)WMl#+$qu;&7+bkLjGxw*OUoXCci@QFl%Z~_s85O{>ldCijS zkY@Hm1caR!Icmy=T4TeESxjkKuUqM05GZuBD{?b(t!`ADxQa|@g;v=+fxwnw1!0_0 zE=+BPV>3zThm}WEC~z@{}Sh7cAMNxC_rSMb?CI@3j^A-wUs`VWOE~2g0zd zR|(vJsztAASS9^2YF4+#2uL~QuBpN`0RzAVX)@;a3O(J_gMoI_k8YBVAnwA|g{vb3 zI&$t#R~N315a`IcJ6&D4Izpf$=l^HATHo66^_~vSu`g-3TC2D{NyYo}2^36AQpK?7 z*?Im(X~gpbphSJi<6LmOAc98lZ7mrD@_@;p1Zvknq)h;UZlSZ7DIZV^fgw# UIViewController { let provider = ContinueCourseProvider( - userCoursesAPI: UserCoursesAPI(), - coursesAPI: CoursesAPI(), - progressesNetworkService: ProgressesNetworkService( - progressesAPI: ProgressesAPI() - ) + userCoursesNetworkService: UserCoursesNetworkService(userCoursesAPI: UserCoursesAPI()), + coursesNetworkService: CoursesNetworkService(coursesAPI: CoursesAPI()), + progressesNetworkService: ProgressesNetworkService(progressesAPI: ProgressesAPI()) ) let presenter = ContinueCoursePresenter() diff --git a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseDataFlow.swift b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseDataFlow.swift index e854fe4132..f4cb58ab58 100644 --- a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseDataFlow.swift +++ b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseDataFlow.swift @@ -8,7 +8,7 @@ enum ContinueCourse { struct Request {} struct Response { - let result: Course + let result: StepikResult } struct ViewModel { @@ -38,6 +38,8 @@ enum ContinueCourse { enum ViewControllerState { case loading + case empty + case error case result(data: ContinueCourseViewModel) } } diff --git a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseInteractor.swift b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseInteractor.swift index 0f6d5af688..e9e9f0dd71 100644 --- a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseInteractor.swift +++ b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseInteractor.swift @@ -41,12 +41,14 @@ final class ContinueCourseInteractor: ContinueCourseInteractorProtocol { self.provider.fetchLastCourse().done { course in if let course = course { self.currentCourse = course - self.presenter.presentLastCourse(response: .init(result: course)) + self.presenter.presentLastCourse(response: .init(result: .success(course))) } else { - self.moduleOutput?.hideContinueCourse() + self.presenter.presentLastCourse(response: .init(result: .failure(Error.noLastCourse))) } }.catch { _ in - self.moduleOutput?.hideContinueCourse() + if self.currentCourse == nil { + self.presenter.presentLastCourse(response: .init(result: .failure(Error.fetchFailed))) + } } } @@ -71,6 +73,11 @@ final class ContinueCourseInteractor: ContinueCourseInteractorProtocol { ) self.tooltipStorageManager.didShowOnHomeContinueLearning = true } + + enum Error: Swift.Error { + case fetchFailed + case noLastCourse + } } extension ContinueCourseInteractor: DataBackUpdateServiceDelegate { @@ -85,7 +92,7 @@ extension ContinueCourseInteractor: DataBackUpdateServiceDelegate { } self.currentCourse = course - self.presenter.presentLastCourse(response: .init(result: course)) + self.presenter.presentLastCourse(response: .init(result: .success(course))) } func dataBackUpdateService( diff --git a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseOutputProtocol.swift b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseOutputProtocol.swift index 43c6f6cd90..f62f971190 100644 --- a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseOutputProtocol.swift +++ b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseOutputProtocol.swift @@ -1,6 +1,5 @@ import Foundation protocol ContinueCourseOutputProtocol: AnyObject { - func hideContinueCourse() func presentLastStep(course: Course, isAdaptive: Bool, viewSource: AnalyticsEvent.CourseViewSource) } diff --git a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCoursePresenter.swift b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCoursePresenter.swift index c540438f44..ef4893f159 100644 --- a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCoursePresenter.swift +++ b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCoursePresenter.swift @@ -9,13 +9,17 @@ final class ContinueCoursePresenter: ContinueCoursePresenterProtocol { weak var viewController: ContinueCourseViewControllerProtocol? func presentLastCourse(response: ContinueCourse.LastCourseLoad.Response) { - var viewModel: ContinueCourse.LastCourseLoad.ViewModel - - viewModel = ContinueCourse.LastCourseLoad.ViewModel( - state: .result(data: self.makeViewModel(course: response.result)) - ) - - self.viewController?.displayLastCourse(viewModel: viewModel) + switch response.result { + case .success(let course): + let viewModel = self.makeViewModel(course: course) + self.viewController?.displayLastCourse(viewModel: .init(state: .result(data: viewModel))) + case .failure(let error): + if case ContinueCourseInteractor.Error.noLastCourse = error { + self.viewController?.displayLastCourse(viewModel: .init(state: .empty)) + } else { + self.viewController?.displayLastCourse(viewModel: .init(state: .error)) + } + } } func presentTooltip(response: ContinueCourse.TooltipAvailabilityCheck.Response) { diff --git a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseProvider.swift b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseProvider.swift index d91e7454fa..6bc97c9a1e 100644 --- a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseProvider.swift +++ b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseProvider.swift @@ -6,49 +6,44 @@ protocol ContinueCourseProviderProtocol { } final class ContinueCourseProvider: ContinueCourseProviderProtocol { - private let userCoursesAPI: UserCoursesAPI - private let coursesAPI: CoursesAPI + private let userCoursesNetworkService: UserCoursesNetworkServiceProtocol + private let coursesNetworkService: CoursesNetworkServiceProtocol private let progressesNetworkService: ProgressesNetworkServiceProtocol init( - userCoursesAPI: UserCoursesAPI, - coursesAPI: CoursesAPI, + userCoursesNetworkService: UserCoursesNetworkServiceProtocol, + coursesNetworkService: CoursesNetworkServiceProtocol, progressesNetworkService: ProgressesNetworkServiceProtocol ) { - self.userCoursesAPI = userCoursesAPI - self.coursesAPI = coursesAPI + self.userCoursesNetworkService = userCoursesNetworkService + self.coursesNetworkService = coursesNetworkService self.progressesNetworkService = progressesNetworkService } func fetchLastCourse() -> Promise { - Promise { seal in - self.userCoursesAPI.retrieve(page: 1).then { result -> Promise<[Course]> in - let lastCourse = result.0 - .sorted(by: { $0.lastViewed > $1.lastViewed }) - .prefix(1) - // [] or [id] - let coursesIDs = lastCourse.compactMap { $0.courseID } - return self.coursesAPI.retrieve(ids: coursesIDs) - }.then { courses -> Promise<(Course?, Progress?)> in - if let course = courses.first, - let progressID = course.progressId { - return self.progressesNetworkService - .fetch(id: progressID) - .map { (course, $0) } - } else { - return Promise.value((nil, nil)) - } - }.done { course, progress in - course?.progress = progress + self.userCoursesNetworkService.fetch().then { userCoursesFetchResult -> Promise<[Course]> in + let lastCourse = userCoursesFetchResult.0 + .sorted(by: { $0.lastViewed > $1.lastViewed }) + .prefix(1) + // [] or [id] + let coursesIDs = lastCourse.compactMap { $0.courseID } + return self.coursesNetworkService.fetch(ids: coursesIDs) + }.then { courses -> Promise<(Course?, Progress?)> in + if let course = courses.first, + let progressID = course.progressId { + return self.progressesNetworkService + .fetch(id: progressID) + .map { (course, $0) } + } else { + return .value((nil, nil)) + } + }.then { course, progress -> Promise in + if let course = course { + course.progress = progress CoreDataHelper.shared.save() - seal.fulfill(course) - }.catch { error in - seal.reject(error) } - } - } - enum Error: Swift.Error { - case lastCourseFetchFailed + return .value(course) + } } } diff --git a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseViewController.swift b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseViewController.swift index f3d0d2cd69..e598a19297 100644 --- a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseViewController.swift +++ b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/ContinueCourseViewController.swift @@ -5,14 +5,13 @@ protocol ContinueCourseViewControllerProtocol: AnyObject { func displayTooltip(viewModel: ContinueCourse.TooltipAvailabilityCheck.ViewModel) } -final class ContinueCourseViewController: UIViewController { +final class ContinueCourseViewController: UIViewController, ControllerWithStepikPlaceholder { private let interactor: ContinueCourseInteractorProtocol - private var state: ContinueCourse.ViewControllerState { - didSet { - self.updateState() - } - } + private var state: ContinueCourse.ViewControllerState + var placeholderContainer = StepikPlaceholderControllerContainer( + appearance: .init(placeholderAppearance: .init(backgroundColor: .clear)) + ) lazy var continueCourseView = self.view as? ContinueCourseView private lazy var continueLearningTooltip = TooltipFactory.continueLearningWidget @@ -42,27 +41,57 @@ final class ContinueCourseViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - self.updateState() + self.registerPlaceholder( + placeholder: StepikPlaceholder( + .tryAgain, + action: { [weak self] in + guard let strongSelf = self else { + return + } + + strongSelf.updateState(newState: .loading) + strongSelf.interactor.doLastCourseRefresh(request: .init()) + } + ), + for: .connectionError + ) + + self.updateState(newState: self.state) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) self.interactor.doLastCourseRefresh(request: .init()) } - private func updateState() { - if case .loading = self.state { + private func updateState(newState: ContinueCourse.ViewControllerState) { + defer { + self.state = newState + } + + self.isPlaceholderShown = false + self.continueCourseView?.hideLoading() + self.continueCourseView?.hideEmpty() + self.continueCourseView?.hideError() + + switch newState { + case .loading: self.continueCourseView?.showLoading() - } else { - self.continueCourseView?.hideLoading() + case .empty: + self.continueCourseView?.showEmpty() + case .error: + self.continueCourseView?.showError() + self.showPlaceholder(for: .connectionError) + case .result(let viewModel): + self.continueCourseView?.configure(viewModel: viewModel) + self.interactor.doTooltipAvailabilityCheck(request: .init()) } } } extension ContinueCourseViewController: ContinueCourseViewControllerProtocol { func displayLastCourse(viewModel: ContinueCourse.LastCourseLoad.ViewModel) { - if case .result(let result) = viewModel.state { - self.continueCourseView?.configure(viewModel: result) - self.interactor.doTooltipAvailabilityCheck(request: .init()) - } - - self.state = viewModel.state + self.updateState(newState: viewModel.state) } func displayTooltip(viewModel: ContinueCourse.TooltipAvailabilityCheck.ViewModel) { @@ -87,7 +116,11 @@ extension ContinueCourseViewController: ContinueCourseViewControllerProtocol { } extension ContinueCourseViewController: ContinueCourseViewDelegate { - func continueCourseContinueButtonDidClick(_ continueCourseView: ContinueCourseView) { + func continueCourseDidClickContinue(_ continueCourseView: ContinueCourseView) { self.interactor.doContinueLastCourseAction(request: .init()) } + + func continueCourseDidClickEmpty(_ continueCourseView: ContinueCourseView) { + DeepLinkRouter.routeToCatalog() + } } diff --git a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueCourseBackgroundView.swift b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueCourseBackgroundView.swift new file mode 100644 index 0000000000..c92fb9b2be --- /dev/null +++ b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueCourseBackgroundView.swift @@ -0,0 +1,63 @@ +import SnapKit +import UIKit + +extension ContinueCourseBackgroundView { + struct Appearance { + let backgroundColor = UIColor.stepikSecondaryBackground + } +} + +final class ContinueCourseBackgroundView: UIView { + let appearance: Appearance + + private lazy var imageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "continue_learning_gradient")) + imageView.contentMode = .scaleAspectFill + return imageView + }() + + init( + frame: CGRect = .zero, + appearance: Appearance = Appearance() + ) { + self.appearance = appearance + super.init(frame: frame) + + self.setupView() + self.addSubviews() + self.makeConstraints() + } + + @available(*, unavailable) + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + + self.performBlockIfAppearanceChanged(from: previousTraitCollection) { + self.updateAppearance() + } + } + + private func updateAppearance() { + self.backgroundColor = self.appearance.backgroundColor + self.imageView.isHidden = self.isDarkInterfaceStyle + } +} + +extension ContinueCourseBackgroundView: ProgrammaticallyInitializableViewProtocol { + func setupView() { + self.updateAppearance() + } + + func addSubviews() { + self.addSubview(self.imageView) + } + + func makeConstraints() { + self.imageView.translatesAutoresizingMaskIntoConstraints = false + self.imageView.snp.makeConstraints { $0.edges.equalToSuperview() } + } +} diff --git a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueCourseEmptyView.swift b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueCourseEmptyView.swift new file mode 100644 index 0000000000..f9945065ff --- /dev/null +++ b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueCourseEmptyView.swift @@ -0,0 +1,101 @@ +import SnapKit +import UIKit + +extension ContinueCourseEmptyView { + struct Appearance { + let primaryColor: UIColor + let defaultInsets = LayoutInsets.default + + let plusIconSize = CGSize(width: 20, height: 20) + let plusContainerCornerRadius: CGFloat = 8 + let plusContainerSize = CGSize(width: 40, height: 40) + var plusContainerBackgroundColor: UIColor { + .dynamic(light: self.primaryColor.withAlphaComponent(0.12), dark: .stepikTertiaryBackground) + } + + let titleFont = UIFont.systemFont(ofSize: 16, weight: .medium) + let titleInsets = LayoutInsets(left: 8) + } +} + +final class ContinueCourseEmptyView: UIControl { + let appearance: Appearance + + private lazy var plusIconImageView: UIImageView = { + let image = UIImage(named: "plus")?.withRenderingMode(.alwaysTemplate) + let imageView = UIImageView(image: image) + imageView.tintColor = self.appearance.primaryColor + imageView.contentMode = .scaleAspectFit + return imageView + }() + private lazy var plusIconContainerView = UIView() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.textColor = self.appearance.primaryColor + label.font = self.appearance.titleFont + label.numberOfLines = 1 + label.text = NSLocalizedString("ContinueCourseEmptyTitle", comment: "") + return label + }() + + override var isHighlighted: Bool { + didSet { + self.plusIconImageView.alpha = self.isHighlighted ? 0.5 : 1.0 + self.titleLabel.alpha = self.isHighlighted ? 0.5 : 1.0 + } + } + + init(frame: CGRect = .zero, appearance: Appearance) { + self.appearance = appearance + super.init(frame: frame) + + self.setupView() + self.addSubviews() + self.makeConstraints() + } + + @available(*, unavailable) + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +extension ContinueCourseEmptyView: ProgrammaticallyInitializableViewProtocol { + func setupView() { + self.plusIconContainerView.backgroundColor = self.appearance.plusContainerBackgroundColor + self.plusIconContainerView.roundAllCorners(radius: self.appearance.plusContainerCornerRadius) + } + + func addSubviews() { + self.addSubview(self.plusIconContainerView) + self.plusIconContainerView.addSubview(self.plusIconImageView) + + self.addSubview(self.titleLabel) + } + + func makeConstraints() { + self.plusIconContainerView.translatesAutoresizingMaskIntoConstraints = false + self.plusIconContainerView.snp.makeConstraints { make in + make.leading + .equalTo(self.safeAreaLayoutGuide.snp.leading) + .offset(self.appearance.defaultInsets.left) + make.centerY.equalToSuperview() + make.size.equalTo(self.appearance.plusContainerSize) + } + + self.plusIconImageView.translatesAutoresizingMaskIntoConstraints = false + self.plusIconImageView.snp.makeConstraints { make in + make.size.equalTo(self.appearance.plusIconSize) + make.center.equalToSuperview() + } + + self.titleLabel.translatesAutoresizingMaskIntoConstraints = false + self.titleLabel.snp.makeConstraints { make in + make.leading + .equalTo(self.plusIconContainerView.snp.trailing) + .offset(self.appearance.titleInsets.left) + make.centerY.equalTo(self.plusIconContainerView.snp.centerY) + } + } +} diff --git a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueCourseView.swift b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueCourseView.swift index 09ddb43aae..f7baff58b1 100644 --- a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueCourseView.swift +++ b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueCourseView.swift @@ -2,15 +2,14 @@ import SnapKit import UIKit protocol ContinueCourseViewDelegate: AnyObject { - func continueCourseContinueButtonDidClick(_ continueCourseView: ContinueCourseView) + func continueCourseDidClickContinue(_ continueCourseView: ContinueCourseView) + func continueCourseDidClickEmpty(_ continueCourseView: ContinueCourseView) } extension ContinueCourseView { struct Appearance { let cornerRadius: CGFloat = 13 - - let coverSize = CGSize(width: 40, height: 40) - let coverCornerRadius: CGFloat = 8 + let primaryColor = UIColor.dynamic(light: .stepikVioletFixed, dark: .stepikSystemPrimaryText) } } @@ -19,8 +18,17 @@ final class ContinueCourseView: UIView { let appearance: Appearance + private lazy var backgroundView = ContinueCourseBackgroundView() + + private lazy var emptyView: ContinueCourseEmptyView = { + let view = ContinueCourseEmptyView(appearance: .init(primaryColor: self.appearance.primaryColor)) + view.addTarget(self, action: #selector(self.emptyViewClicked), for: .touchUpInside) + view.isHidden = true + return view + }() + private lazy var lastStepView: ContinueLastStepView = { - let view = ContinueLastStepView() + let view = ContinueLastStepView(appearance: .init(primaryColor: self.appearance.primaryColor)) view.addTarget(self, action: #selector(self.lastStepViewClicked), for: .touchUpInside) return view }() @@ -57,31 +65,60 @@ final class ContinueCourseView: UIView { } func showLoading() { - self.lastStepView.setContentHidden(true) + self.lastStepView.isHidden = true self.skeleton.viewBuilder = { ContinueCourseSkeletonView() } self.skeleton.show() } func hideLoading() { - self.lastStepView.setContentHidden(false) + self.lastStepView.isHidden = false self.skeleton.hide() } + func showEmpty() { + self.lastStepView.isHidden = true + self.emptyView.isHidden = false + } + + func hideEmpty() { + self.lastStepView.isHidden = false + self.emptyView.isHidden = true + } + + func showError() { + self.lastStepView.isHidden = true + } + + func hideError() { + self.lastStepView.isHidden = false + } + @objc private func lastStepViewClicked() { - self.delegate?.continueCourseContinueButtonDidClick(self) + self.delegate?.continueCourseDidClickContinue(self) + } + + @objc + private func emptyViewClicked() { + self.delegate?.continueCourseDidClickEmpty(self) } } extension ContinueCourseView: ProgrammaticallyInitializableViewProtocol { func addSubviews() { + self.addSubview(self.backgroundView) + self.addSubview(self.emptyView) self.addSubview(self.lastStepView) } func makeConstraints() { - self.lastStepView.translatesAutoresizingMaskIntoConstraints = false - self.lastStepView.snp.makeConstraints { make in - make.edges.equalToSuperview() + [ + self.backgroundView, + self.emptyView, + self.lastStepView + ].forEach { view in + view.translatesAutoresizingMaskIntoConstraints = false + view.snp.makeConstraints { $0.edges.equalToSuperview() } } } } diff --git a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueLastStepView.swift b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueLastStepView.swift index 995c21185a..170fd09af8 100644 --- a/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueLastStepView.swift +++ b/Stepic/Sources/Modules/ExploreSubmodules/ContinueCourse/Views/ContinueLastStepView.swift @@ -3,8 +3,7 @@ import UIKit extension ContinueLastStepView { struct Appearance { - let primaryColor = UIColor.dynamic(light: .stepikVioletFixed, dark: .stepikSystemPrimaryText) - let backgroundColor = UIColor.stepikSecondaryBackground + let primaryColor: UIColor let defaultInsets = LayoutInsets.default let coverCornerRadius: CGFloat = 8 @@ -60,12 +59,6 @@ final class ContinueLastStepView: UIControl { return imageView }() - private lazy var backgroundImageView: UIImageView = { - let imageView = UIImageView(image: UIImage(named: "continue_learning_gradient")) - imageView.contentMode = .scaleAspectFill - return imageView - }() - var courseTitle: String? { didSet { self.courseNameLabel.text = self.courseTitle @@ -92,26 +85,16 @@ final class ContinueLastStepView: UIControl { var tooltipAnchorView: UIView { self.rightDetailImageView } - private var contentViews: [UIView] { - [ - self.coverImageView, - self.courseNameLabel, - self.statsView, - self.rightDetailImageView - ] - } - override var isHighlighted: Bool { didSet { - self.contentViews.forEach { $0.alpha = self.isHighlighted ? 0.5 : 1.0 } + self.subviews.forEach { $0.alpha = self.isHighlighted ? 0.5 : 1.0 } } } - init(frame: CGRect = .zero, appearance: Appearance = Appearance()) { + init(frame: CGRect = .zero, appearance: Appearance) { self.appearance = appearance super.init(frame: frame) - self.setupView() self.addSubviews() self.makeConstraints() } @@ -121,23 +104,6 @@ final class ContinueLastStepView: UIControl { fatalError("init(coder:) has not been implemented") } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - self.performBlockIfAppearanceChanged(from: previousTraitCollection) { - self.updateBackground() - } - } - - func setContentHidden(_ isHidden: Bool) { - self.contentViews.forEach { $0.isHidden = isHidden } - } - - private func updateBackground() { - self.backgroundColor = self.appearance.backgroundColor - self.backgroundImageView.isHidden = self.isDarkInterfaceStyle - } - private func updateProgress() { self.statsView.updateProgress( viewModel: .init(progress: self.progress, progressLabelText: self.progressText ?? "") @@ -146,24 +112,11 @@ final class ContinueLastStepView: UIControl { } extension ContinueLastStepView: ProgrammaticallyInitializableViewProtocol { - func setupView() { - self.updateBackground() - } - func addSubviews() { - self.addSubviews([ - self.backgroundImageView, - self.coverImageView, - self.courseNameLabel, - self.statsView, - self.rightDetailImageView - ]) + self.addSubviews([self.coverImageView, self.courseNameLabel, self.statsView, self.rightDetailImageView]) } func makeConstraints() { - self.backgroundImageView.translatesAutoresizingMaskIntoConstraints = false - self.backgroundImageView.snp.makeConstraints { $0.edges.equalToSuperview() } - self.coverImageView.translatesAutoresizingMaskIntoConstraints = false self.coverImageView.snp.makeConstraints { make in make.leading diff --git a/Stepic/Sources/Modules/Home/HomeInteractor.swift b/Stepic/Sources/Modules/Home/HomeInteractor.swift index 574b1d279c..66f449131d 100644 --- a/Stepic/Sources/Modules/Home/HomeInteractor.swift +++ b/Stepic/Sources/Modules/Home/HomeInteractor.swift @@ -107,13 +107,4 @@ extension HomeInteractor: StoriesOutputProtocol { } } -extension HomeInteractor: ContinueCourseOutputProtocol { - func hideContinueCourse() { - self.homePresenter?.presentCourseListState( - response: .init( - module: Home.Submodule.continueCourse, - result: .empty - ) - ) - } -} +extension HomeInteractor: ContinueCourseOutputProtocol {} diff --git a/Stepic/Sources/Modules/Home/HomeViewController.swift b/Stepic/Sources/Modules/Home/HomeViewController.swift index b2b0ec3e86..76db5f9ede 100644 --- a/Stepic/Sources/Modules/Home/HomeViewController.swift +++ b/Stepic/Sources/Modules/Home/HomeViewController.swift @@ -542,14 +542,13 @@ extension HomeViewController: HomeViewControllerProtocol { let shouldDisplayStories = strongSelf.currentStoriesSubmoduleState == .shown || (strongSelf.currentStoriesSubmoduleState == .hidden && strongSelf.lastContentLanguage != viewModel.contentLanguage) - let shouldDisplayContinueCourse = viewModel.isAuthorized let shouldDisplayAnonymousPlaceholder = !viewModel.isAuthorized strongSelf.lastContentLanguage = viewModel.contentLanguage strongSelf.lastIsAuthorizedFlag = viewModel.isAuthorized strongSelf.refreshStateForStories(state: shouldDisplayStories ? .shown : .hidden) - strongSelf.refreshContinueCourse(state: shouldDisplayContinueCourse ? .shown : .hidden) + strongSelf.refreshContinueCourse(state: .shown) strongSelf.refreshStateForEnrolledCourses(state: shouldDisplayAnonymousPlaceholder ? .anonymous : .normal) strongSelf.refreshStateForVisitedCourses(state: .shown) strongSelf.refreshStateForPopularCourses(state: .normal) diff --git a/Stepic/Sources/Services/ApplicationShortcutService.swift b/Stepic/Sources/Services/ApplicationShortcutService.swift index 7569496ffe..396541e3eb 100644 --- a/Stepic/Sources/Services/ApplicationShortcutService.swift +++ b/Stepic/Sources/Services/ApplicationShortcutService.swift @@ -24,8 +24,8 @@ final class ApplicationShortcutService: ApplicationShortcutServiceProtocol { coursesPersistenceService: CoursesPersistenceServiceProtocol = CoursesPersistenceService(), adaptiveStorageManager: AdaptiveStorageManagerProtocol = AdaptiveStorageManager(), continueCourseProvider: ContinueCourseProviderProtocol = ContinueCourseProvider( - userCoursesAPI: UserCoursesAPI(), - coursesAPI: CoursesAPI(), + userCoursesNetworkService: UserCoursesNetworkService(userCoursesAPI: UserCoursesAPI()), + coursesNetworkService: CoursesNetworkService(coursesAPI: CoursesAPI()), progressesNetworkService: ProgressesNetworkService(progressesAPI: ProgressesAPI()) ), userAccountService: UserAccountServiceProtocol = UserAccountService(), diff --git a/Stepic/en.lproj/Localizable.strings b/Stepic/en.lproj/Localizable.strings index c4d222794e..bd535b15da 100644 --- a/Stepic/en.lproj/Localizable.strings +++ b/Stepic/en.lproj/Localizable.strings @@ -1011,6 +1011,8 @@ EditStepRemoteUpdateUnsuccessfulTitle = "Failed to update"; CourseWidgetArchived = "Archived"; CourseWidgetSeeAllTitle = "See All"; +ContinueCourseEmptyTitle = "Find your first course"; + /* Course List Filter */ CourseListFilterTitle = "Filters"; CourseListFilterResetButtonTitle = "Reset"; diff --git a/Stepic/ru.lproj/Localizable.strings b/Stepic/ru.lproj/Localizable.strings index 7203b267c2..132753f40f 100644 --- a/Stepic/ru.lproj/Localizable.strings +++ b/Stepic/ru.lproj/Localizable.strings @@ -1012,6 +1012,8 @@ EditStepRemoteUpdateUnsuccessfulTitle = "Не удалось обновить"; CourseWidgetArchived = "В архиве"; CourseWidgetSeeAllTitle = "Посмотреть все"; +ContinueCourseEmptyTitle = "Найдите свой первый курс"; + /* Course List Filter */ CourseListFilterTitle = "Фильтры"; CourseListFilterResetButtonTitle = "Сбросить";