Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved unit navigation #448

Merged
merged 5 commits into from
Feb 20, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Stepic.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,7 @@
2C0AF18C203C67EC000EA3B6 /* MigrationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C0AF18B203C67EC000EA3B6 /* MigrationExtensions.swift */; };
2C1000802029BA7F00E55597 /* BaseCardsStepsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C5D51582024653B00B9D932 /* BaseCardsStepsViewController.swift */; };
2C104B682069064D0026FEB9 /* autocomplete_suggestions.plist in Resources */ = {isa = PBXBuildFile; fileRef = 2C104B672069064D0026FEB9 /* autocomplete_suggestions.plist */; };
2C10C477221AB8CC00FA3E13 /* UnitNavigationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C10C476221AB8CC00FA3E13 /* UnitNavigationService.swift */; };
2C1219901F9655AB00A43E98 /* NotificationsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C12198F1F9655AB00A43E98 /* NotificationsSection.swift */; };
2C130E892125A3F50022E998 /* AssemblyFactoryMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C130E882125A3F50022E998 /* AssemblyFactoryMock.swift */; };
2C130E8B2125A4410022E998 /* ApplicationAssemblyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C130E8A2125A4410022E998 /* ApplicationAssemblyMock.swift */; };
Expand Down Expand Up @@ -2630,9 +2631,9 @@
2C8A0F2220FC7CD80009F67E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 08A1257B1BDEBCC90066B2B2 /* Localizable.strings */; };
2C8A0F3320FCCE230009F67E /* VKSocialSDKProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088E58C31DE34ED20009B9CE /* VKSocialSDKProvider.swift */; };
2C8A0F3620FCCE650009F67E /* SocialSDKProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088E58C01DE34E2F0009B9CE /* SocialSDKProvider.swift */; };
2C8AD7662208646100C9C089 /* DownloadsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0889FEAE1BFB771200C6417E /* DownloadsViewController.swift */; };
2C8AD758220311F000C9C089 /* DataBackUpdateService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8AD757220311F000C9C089 /* DataBackUpdateService.swift */; };
2C8AD7622204932300C9C089 /* CourseSubscriptionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8AD7612204932200C9C089 /* CourseSubscriptionManager.swift */; };
2C8AD7662208646100C9C089 /* DownloadsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0889FEAE1BFB771200C6417E /* DownloadsViewController.swift */; };
2C8C96EA211DE34700A9FB99 /* RateAppViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 089056101E98021000B8FE6A /* RateAppViewController.xib */; };
2C8CB0E31FB48F39008CB1AC /* EnrollmentsAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8CB0E21FB48F39008CB1AC /* EnrollmentsAPI.swift */; };
2C905230212E9C9300354DFB /* CoursePlainObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C90522F212E9C9300354DFB /* CoursePlainObjectTests.swift */; };
Expand Down Expand Up @@ -5570,6 +5571,7 @@
2C09313621D00B8D002B605D /* VideoDownloadingService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoDownloadingService.swift; sourceTree = "<group>"; };
2C0AF18B203C67EC000EA3B6 /* MigrationExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationExtensions.swift; sourceTree = "<group>"; };
2C104B672069064D0026FEB9 /* autocomplete_suggestions.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = autocomplete_suggestions.plist; sourceTree = "<group>"; };
2C10C476221AB8CC00FA3E13 /* UnitNavigationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitNavigationService.swift; sourceTree = "<group>"; };
2C12198F1F9655AB00A43E98 /* NotificationsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsSection.swift; sourceTree = "<group>"; };
2C130E882125A3F50022E998 /* AssemblyFactoryMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssemblyFactoryMock.swift; sourceTree = "<group>"; };
2C130E8A2125A4410022E998 /* ApplicationAssemblyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationAssemblyMock.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -9914,6 +9916,7 @@
089877B121404CF00065DFA2 /* StringStorageServiceProtocol.swift */,
2C23C12621AD8907001DCF89 /* NextLessonService.swift */,
2C8AD757220311F000C9C089 /* DataBackUpdateService.swift */,
2C10C476221AB8CC00FA3E13 /* UnitNavigationService.swift */,
);
path = Services;
sourceTree = "<group>";
Expand Down Expand Up @@ -15459,6 +15462,7 @@
73DB05040F3345F6568DCCBB /* FullscreenCourseListView.swift in Sources */,
62E983594B255BB6BE92AD8A /* UniqueIdentifierType.swift in Sources */,
62E9850F9C459CC122A416F3 /* ContentLanguageSwitchAvailabilityService.swift in Sources */,
2C10C477221AB8CC00FA3E13 /* UnitNavigationService.swift in Sources */,
62E9870883D5C6A1DCB15E4B /* TagsOutputProtocol.swift in Sources */,
62E9893ECE0FA9E7AEC2B6DD /* CourseListCollectionOutputProtocol.swift in Sources */,
62E98236342BF7C367FB6F36 /* Array+reordering.swift in Sources */,
Expand Down
182 changes: 145 additions & 37 deletions Stepic/LessonPresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import Foundation
import UIKit
import SVProgressHUD
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Давай попробуем продумать приложение так, чтобы от блочащих экран SVProgressHUD избавиться.
(Естественно, не в рамках этого PR)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Пока всё не перепишем, об этом рано думать :)


enum LessonViewState {
case displayingSteps, placeholder, refreshing
Expand All @@ -28,6 +29,8 @@ class LessonPresenter {
static let stepUpdatedNotification = "StepUpdatedNotification"
fileprivate var tabViewsForStepId = [Int: UIView]()

private var controllerForIndex: [Int: UIViewController] = [:]

fileprivate var lesson: Lesson?
fileprivate var startStepId: Int = 0
fileprivate var stepId: Int?
Expand All @@ -37,9 +40,6 @@ class LessonPresenter {
var stepsAPI: StepsAPI
var lessonsAPI: LessonsAPI

var shouldNavigateToPrev: Bool = false
var shouldNavigateToNext: Bool = false

fileprivate var didInitSteps: Bool = false
fileprivate var didSelectTab: Bool = false

Expand All @@ -55,6 +55,24 @@ class LessonPresenter {
return service
}()

private lazy var unitNavigationService: UnitNavigationServiceProtocol = {
let service = UnitNavigationService(
sectionsPersistenceService: SectionsPersistenceService(),
sectionsNetworkService: SectionsNetworkService(sectionsAPI: SectionsAPI()),
unitsPersistenceService: UnitsPersistenceService(),
unitsNetworkService: UnitsNetworkService(unitsAPI: UnitsAPI()),
coursesPersistenceService: CoursesPersistenceService(),
coursesNetworkService: CoursesNetworkService(coursesAPI: CoursesAPI())
)
return service
}()

private var didNextUnitLoad = false
private var nextUnit: Unit?

private var didPreviousUnitLoad = false
private var previousUnit: Unit?

init(objects: LessonInitObjects?, ids: LessonInitIds?, stepsAPI: StepsAPI, lessonsAPI: LessonsAPI) {
if let objects = objects {
self.lesson = objects.lesson
Expand All @@ -76,6 +94,52 @@ class LessonPresenter {
name: .stepDone,
object: nil
)

if let unitID = self.unitId {
DispatchQueue.global().async { [weak self] in
guard let strongSelf = self else {
return
}

strongSelf.unitNavigationService.findUnitForNavigation(
from: unitID,
direction: .next
).done { unit in
print("next unit loaded, unit = \(unit?.id)")
if let unit = unit {
kvld marked this conversation as resolved.
Show resolved Hide resolved
if let stepsCount = strongSelf.lesson?.stepsArray.count {
(strongSelf.controllerForIndex[stepsCount - 1] as? VideoStepViewController)?.nextLessonHandler = {
strongSelf.navigateToNextOrPreviousUnit(direction: .next)
}
(strongSelf.controllerForIndex[stepsCount - 1] as? WebStepViewController)?.nextLessonHandler = {
strongSelf.navigateToNextOrPreviousUnit(direction: .next)
}
}

strongSelf.didNextUnitLoad = true
strongSelf.nextUnit = unit
}
}.cauterize()

strongSelf.unitNavigationService.findUnitForNavigation(
from: unitID,
direction: .previous
).done { unit in
print("previous unit loaded, unit = \(unit?.id)")
if let unit = unit {
kvld marked this conversation as resolved.
Show resolved Hide resolved
(strongSelf.controllerForIndex[0] as? VideoStepViewController)?.prevLessonHandler = {
strongSelf.navigateToNextOrPreviousUnit(direction: .previous)
}
(strongSelf.controllerForIndex[0] as? WebStepViewController)?.prevLessonHandler = {
strongSelf.navigateToNextOrPreviousUnit(direction: .previous)
}

strongSelf.didPreviousUnitLoad = true
strongSelf.previousUnit = unit
}
}.cauterize()
}
}
}

deinit {
Expand Down Expand Up @@ -165,14 +229,6 @@ class LessonPresenter {
return
}

if let section = lesson?.unit?.section,
let unitId = unitId {
if let index = section.unitsArray.index(of: unitId) {
shouldNavigateToPrev = shouldNavigateToPrev || (index != 0)
shouldNavigateToNext = shouldNavigateToNext || (index < section.unitsArray.count - 1)
}
}

view?.updateTitle(title: lesson?.title ?? NSLocalizedString("Lesson", comment: ""))

if let stepId = stepId {
Expand Down Expand Up @@ -298,26 +354,25 @@ class LessonPresenter {
self?.canSendViews ?? false
}

if context == .unit {
if index == 0 && shouldNavigateToPrev {
stepController.prevLessonHandler = {
[weak self] in
if let unitID = self?.unitId {
self?.sectionNavigationDelegate?.didRequestPreviousUnitPresentationForLessonInUnit(unitID: unitID)
}
if context == .unit && index == 0 && didPreviousUnitLoad {
stepController.prevLessonHandler = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.navigateToNextOrPreviousUnit(direction: .previous)
}
}

if index == lesson.steps.count - 1 && shouldNavigateToNext {
stepController.nextLessonHandler = {
[weak self] in
if let unitID = self?.unitId {
self?.sectionNavigationDelegate?.didRequestNextUnitPresentationForLessonInUnit(unitID: unitID)
}
if context == .unit && index == lesson.stepsArray.count - 1 && didNextUnitLoad {
stepController.nextLessonHandler = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.navigateToNextOrPreviousUnit(direction: .next)
}
}

controllerForIndex[index] = stepController
return stepController
} else {
let stepController = ControllerHelper.instantiateViewController(identifier: "WebStepViewController") as! WebStepViewController
Expand Down Expand Up @@ -345,30 +400,83 @@ class LessonPresenter {
self?.canSendViews ?? false
}
stepController.lessonSlug = lesson.slug
if context == .unit {
if index == 0 && shouldNavigateToPrev {
stepController.prevLessonHandler = {
[weak self] in
if let unitID = self?.unitId {
self?.sectionNavigationDelegate?.didRequestPreviousUnitPresentationForLessonInUnit(unitID: unitID)
}

if context == .unit && index == 0 && didPreviousUnitLoad {
stepController.prevLessonHandler = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.navigateToNextOrPreviousUnit(direction: .previous)
}
}

if index == lesson.steps.count - 1 && shouldNavigateToNext {
stepController.nextLessonHandler = {
[weak self] in
if let unitID = self?.unitId {
self?.sectionNavigationDelegate?.didRequestNextUnitPresentationForLessonInUnit(unitID: unitID)
}
if context == .unit && index == lesson.stepsArray.count - 1 && didNextUnitLoad {
stepController.nextLessonHandler = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.navigateToNextOrPreviousUnit(direction: .next)
}
}

controllerForIndex[index] = stepController
return stepController
}
}

private func navigateToNextOrPreviousUnit(direction: UnitNavigationDirection) {
guard let targetUnit = direction == .next ? self.nextUnit : self.previousUnit else {
return
}

SVProgressHUD.show()
let cachedLesson = Lesson.fetch([targetUnit.lessonId]).first
if let lesson = cachedLesson {
self.replaceByNewLesson(
lesson: lesson,
unit: targetUnit,
stepArrayFunction: { direction == .next ? $0.first : $0.last }
)
} else {
self.lessonsAPI.retrieve(ids: [targetUnit.lessonId]).done { lessons in
guard let lesson = lessons.first else {
SVProgressHUD.showError(withStatus: nil)
return
}

self.replaceByNewLesson(
lesson: lesson,
unit: targetUnit,
stepArrayFunction: { direction == .next ? $0.first : $0.last }
)
SVProgressHUD.dismiss()
}.catch { _ in
print("error while fetching lesson for next/prev unit")
SVProgressHUD.showError(withStatus: nil)
}
}
}

private func replaceByNewLesson(lesson: Lesson, unit: Unit, stepArrayFunction: ([Int]) -> Int?) {
guard let viewControllers = self.view?.nController?.viewControllers,
let presentingViewController = self.view?.nController?.viewControllers[safe: viewControllers.count - 2] else {
return
}

guard let stepID = stepArrayFunction(lesson.stepsArray) else {
SVProgressHUD.showError(withStatus: nil)
return
}

let newLessonController = LessonLegacyAssembly(
initObjects: nil,
initIDs: (stepId: stepID, unitId: unit.id)
).makeModule()

SVProgressHUD.dismiss()
presentingViewController.replace(by: newLessonController)
}

func tabView(index: Int) -> UIView {
guard lesson != nil else {
return UIView()
Expand Down
20 changes: 1 addition & 19 deletions Stepic/LessonViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,13 @@ import SnapKit
final class LessonLegacyAssembly: Assembly {
private let initObjects: LessonInitObjects?
private let initIDs: LessonInitIds?
private let navigationRules: LessonNavigationRules
private let navigationDelegate: SectionNavigationDelegate

init(
initObjects: LessonInitObjects?,
initIDs: LessonInitIds?,
navigationRules: LessonNavigationRules,
navigationDelegate: SectionNavigationDelegate
initIDs: LessonInitIds?
) {
self.initObjects = initObjects
self.initIDs = initIDs
self.navigationRules = navigationRules
self.navigationDelegate = navigationDelegate
}

func makeModule() -> UIViewController {
Expand All @@ -37,9 +31,6 @@ final class LessonLegacyAssembly: Assembly {
lessonVC.initObjects = self.initObjects
lessonVC.initIds = self.initIDs

lessonVC.navigationRules = self.navigationRules
lessonVC.sectionNavigationDelegate = self.navigationDelegate

return lessonVC
}
}
Expand All @@ -50,10 +41,6 @@ class LessonViewController: PagerController, ShareableController, LessonView {

var parentShareBlock: ((UIActivityViewController) -> Void)?

weak var sectionNavigationDelegate: SectionNavigationDelegate?

var navigationRules : LessonNavigationRules?

fileprivate var presenter: LessonPresenter?

lazy var activityView: UIView = self.initActivityView()
Expand Down Expand Up @@ -145,11 +132,6 @@ class LessonViewController: PagerController, ShareableController, LessonView {

presenter = LessonPresenter(objects: initObjects, ids: initIds, stepsAPI: ApiDataDownloader.steps, lessonsAPI: ApiDataDownloader.lessons)
presenter?.view = self
presenter?.sectionNavigationDelegate = sectionNavigationDelegate
if let rules = navigationRules {
presenter?.shouldNavigateToPrev = rules.prev
presenter?.shouldNavigateToNext = rules.next
}
presenter?.refreshSteps()
}

Expand Down
4 changes: 0 additions & 4 deletions Stepic/Modules/CourseInfo/CourseInfoDataFlow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,12 @@ enum CourseInfo {
struct Response {
let lesson: Lesson
let unitID: Unit.IdType
let navigationRules: LessonNavigationRules
let navigationDelegate: SectionNavigationDelegate
}

@available(*, deprecated, message: "Old ugly Lesson controller initialization")
struct ViewModel {
let initObjects: LessonInitObjects
let initIDs: LessonInitIds
let navigationRules: LessonNavigationRules
let navigationDelegate: SectionNavigationDelegate
}
}

Expand Down
15 changes: 2 additions & 13 deletions Stepic/Modules/CourseInfo/CourseInfoInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -261,23 +261,12 @@ final class CourseInfoInteractor: CourseInfoInteractorProtocol {
}

extension CourseInfoInteractor: CourseInfoTabSyllabusOutputProtocol {
func presentLesson(
in unit: Unit,
navigationDelegate: SectionNavigationDelegate,
navigationRules: LessonNavigationRules
) {
func presentLesson(in unit: Unit) {
guard let lesson = unit.lesson else {
return
}

self.presenter.presentLesson(
response: .init(
lesson: lesson,
unitID: unit.id,
navigationRules: navigationRules,
navigationDelegate: navigationDelegate
)
)
self.presenter.presentLesson(response: .init(lesson: lesson, unitID: unit.id))
}

func presentPersonalDeadlinesCreation(for course: Course) {
Expand Down
Loading