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

New course info screen #423

Merged
merged 45 commits into from
Jan 29, 2019
Merged
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
585d7a0
Course info: info tab UI (#406)
ivan-magda Nov 29, 2018
510c826
Course info: container UI & header (#404)
kvld Nov 30, 2018
2a646b3
Course info: modules UI (#415)
kvld Nov 30, 2018
629bd23
Course info: info tab logic (#411)
ivan-magda Dec 6, 2018
adb4dc9
Course info: container logic (#426) & syllabus (#438)
kvld Dec 28, 2018
b93974d
Sections deadlines
kvld Dec 28, 2018
c7e1cb4
Layout fixes
kvld Jan 9, 2019
1a3d332
Completion & failure events
kvld Jan 9, 2019
a5eb3ce
Add enrolled state
kvld Jan 10, 2019
bf8f70b
Fix deep link & last step routers
kvld Jan 10, 2019
5718f42
Next & previous units service (#422)
kvld Jan 10, 2019
c5fa25f
Present controller with lesson
kvld Jan 10, 2019
3066f30
Fixes for SDK 11
kvld Jan 10, 2019
1b99ddb
Set build for current branch
kvld Jan 10, 2019
0670afa
Fix navigation bar issues
kvld Jan 11, 2019
488ad67
Personal deadlines
kvld Jan 13, 2019
d2732fd
Fix after rename
kvld Jan 13, 2019
30aecc8
Exam in syllabus
kvld Jan 13, 2019
10e7c6c
"More actions" alert & course sharing
kvld Jan 13, 2019
9207d0e
Main action & drop course
kvld Jan 14, 2019
59a0890
Merge branch 'dev' into feature/course-info
kvld Jan 14, 2019
374aae4
Set version to 1.75 & increment build
kvld Jan 14, 2019
4212360
Add tap proxy view for download buttons
kvld Jan 14, 2019
5e9a8a3
Fix deadlines view
kvld Jan 14, 2019
1a5c3f2
Next / previous lesson
kvld Jan 14, 2019
434694a
Hide syllabus tab for adaptive courses
kvld Jan 21, 2019
ef3cc04
Personal deadlines tooltip
kvld Jan 21, 2019
8d87119
Make enabled-disabled visual state for cells
kvld Jan 21, 2019
9f87e16
Remove "Downloads" from Settings
kvld Jan 21, 2019
df750c3
"Download all" logic
kvld Jan 21, 2019
be3b0f5
Fix new discussion layout (12 sdk)
kvld Jan 23, 2019
e526064
Fix video quality (12 sdk)
kvld Jan 23, 2019
0ecd7ad
Fix certificates layout (12 sdk)
kvld Jan 23, 2019
cbdd146
Remove action button tab
kvld Jan 23, 2019
a44aec8
Fix semaphore dispose crash
kvld Jan 23, 2019
1f2fddf
Update button state after remove
kvld Jan 23, 2019
8edefbe
Set version to 1.76 & increment build
kvld Jan 24, 2019
4246a2e
Fixed downloads
kvld Jan 28, 2019
f5f4386
Force populate sections
kvld Jan 28, 2019
dfad0c7
Fix empty units download state error
kvld Jan 28, 2019
7f5933a
Analytics & small fixes
kvld Jan 28, 2019
e926899
Remove unused code
kvld Jan 28, 2019
3b0cbe6
Fix iPad & landscape
kvld Jan 28, 2019
3f1a307
Bump build
kvld Jan 28, 2019
48c8fad
Fixes
kvld Jan 28, 2019
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
Prev Previous commit
Next Next commit
Main action & drop course
kvld committed Jan 14, 2019
commit 9207d0e32e52a4c0ad0cb451897f87e36e81624a
13 changes: 9 additions & 4 deletions Stepic/LastStepRouter.swift
Original file line number Diff line number Diff line change
@@ -20,7 +20,8 @@ class LastStepRouter {
for course: Course,
isAdaptive: Bool? = nil,
didJustSubscribe: Bool = false,
using navigationController: UINavigationController
using navigationController: UINavigationController,
skipSyllabus: Bool = false
) {
guard let lastStepId = course.lastStepId else {
return
@@ -35,7 +36,7 @@ class LastStepRouter {
course.lastStep = newLastStep
CoreDataHelper.instance.save()
}.ensure {
self.navigate(for: course, isAdaptive: isAdaptive, didJustSubscribe: didJustSubscribe, using: navigationController)
self.navigate(for: course, isAdaptive: isAdaptive, didJustSubscribe: didJustSubscribe, using: navigationController, skipSyllabus: skipSyllabus)
}.catch {
_ in
print("error while updating lastStep")
@@ -46,7 +47,8 @@ class LastStepRouter {
for course: Course,
isAdaptive: Bool?,
didJustSubscribe: Bool,
using navigationController: UINavigationController
using navigationController: UINavigationController,
skipSyllabus: Bool = false
) {
let shouldOpenInAdaptiveMode = isAdaptive ?? AdaptiveStorageManager.shared.canOpenInAdaptiveMode(courseId: course.id)
if shouldOpenInAdaptiveMode {
@@ -120,7 +122,10 @@ class LastStepRouter {
// lessonVC.sectionNavigationDelegate = unitsVC

SVProgressHUD.showSuccess(withStatus: "")
navigationController.pushViewController(sectionsVC, animated: false)

if !skipSyllabus {
navigationController.pushViewController(sectionsVC, animated: false)
}
navigationController.pushViewController(lessonVC, animated: true)

LocalProgressLastViewedUpdater.shared.updateView(for: course)
3 changes: 2 additions & 1 deletion Stepic/Modules/CourseInfo/CourseInfoAssembly.swift
Original file line number Diff line number Diff line change
@@ -36,7 +36,8 @@ final class CourseInfoAssembly: Assembly {
provider: provider,
networkReachabilityService: NetworkReachabilityService(),
courseSubscriber: CourseSubscriber(),
userAccountService: UserAccountService()
userAccountService: UserAccountService(),
adaptiveStorageManager: AdaptiveStorageManager()
)

let viewController = CourseInfoViewController(
15 changes: 15 additions & 0 deletions Stepic/Modules/CourseInfo/CourseInfoDataFlow.swift
Original file line number Diff line number Diff line change
@@ -101,6 +101,21 @@ enum CourseInfo {
}
}

/// Present last step in course
enum PresentLastStep {
struct Response {
let course: Course
let isAdaptive: Bool
}

struct ViewModel {
@available(*, deprecated, message: "Target modules can't be initialized w/o model")
let course: Course
@available(*, deprecated, message: "Target modules can't be initialized w/o model")
let isAdaptive: Bool
}
}

// MARK: States

enum ViewControllerState {
72 changes: 71 additions & 1 deletion Stepic/Modules/CourseInfo/CourseInfoInteractor.swift
Original file line number Diff line number Diff line change
@@ -12,6 +12,8 @@ import PromiseKit
protocol CourseInfoInteractorProtocol {
func refreshCourse()
func shareCourse()
func dropCourse()
func doMainCourseAction()
func tryToSetOnlineMode()

func registerSubmodules(request: CourseInfo.RegisterSubmodule.Request)
@@ -23,6 +25,7 @@ final class CourseInfoInteractor: CourseInfoInteractorProtocol {
let networkReachabilityService: NetworkReachabilityServiceProtocol
let courseSubscriber: CourseSubscriberProtocol
let userAccountService: UserAccountServiceProtocol
let adaptiveStorageManager: AdaptiveStorageManagerProtocol

private let courseID: Course.IdType
private var currentCourse: Course? {
@@ -63,13 +66,15 @@ final class CourseInfoInteractor: CourseInfoInteractorProtocol {
provider: CourseInfoProviderProtocol,
networkReachabilityService: NetworkReachabilityServiceProtocol,
courseSubscriber: CourseSubscriberProtocol,
userAccountService: UserAccountServiceProtocol
userAccountService: UserAccountServiceProtocol,
adaptiveStorageManager: AdaptiveStorageManagerProtocol
) {
self.presenter = presenter
self.provider = provider
self.networkReachabilityService = networkReachabilityService
self.courseSubscriber = courseSubscriber
self.userAccountService = userAccountService
self.adaptiveStorageManager = adaptiveStorageManager
self.courseID = courseID
}

@@ -113,6 +118,71 @@ final class CourseInfoInteractor: CourseInfoInteractorProtocol {
self.presenter.presentCourseSharing(response: .init(urlPath: urlPath))
}

func dropCourse() {
guard let course = self.currentCourse, course.enrolled else {
return
}

self.presenter.presentWaitingState()
self.courseSubscriber.leave(course: course, source: .preview).done { course in
// Refresh course
self.currentCourse = course
self.presenter.presentCourse(response: .init(result: .success(course)))
}.ensure {
self.presenter.dismissWaitingState()
}.catch { error in
print("course info interactor: drop course error = \(error)")
}
}

func doMainCourseAction() {
guard let course = self.currentCourse else {
return
}

self.presenter.presentWaitingState()

if !self.userAccountService.isAuthorized {
self.presenter.dismissWaitingState()
self.presenter.presentAuthorization()
return
}

if course.enrolled {
// Enrolled course -> open last step
self.presenter.dismissWaitingState()
self.presenter.presentLastStep(
response: .init(
course: course,
isAdaptive: self.adaptiveStorageManager.canOpenInAdaptiveMode(
courseId: course.id
)
)
)
} else {
// Unenrolled course -> join, open last step
self.courseSubscriber.join(course: course, source: .preview).done { course in
// Refresh course
self.currentCourse = course
self.presenter.presentCourse(response: .init(result: .success(course)))

// Present step
self.presenter.presentLastStep(
response: .init(
course: course,
isAdaptive: self.adaptiveStorageManager.canOpenInAdaptiveMode(
courseId: course.id
)
)
)
}.ensure {
self.presenter.dismissWaitingState()
}.catch { error in
print("course info interactor: join course error = \(error)")
}
}
}

// MARK: Private methods

private func fetchCourseInAppropriateMode() -> Promise<CourseInfo.ShowCourse.Response> {
26 changes: 26 additions & 0 deletions Stepic/Modules/CourseInfo/CourseInfoPresenter.swift
Original file line number Diff line number Diff line change
@@ -14,6 +14,11 @@ protocol CourseInfoPresenterProtocol {
func presentPersonalDeadlinesSettings(response: CourseInfo.PersonalDeadlinesSettings.Response)
func presentExamLesson(response: CourseInfo.ShowExamLesson.Response)
func presentCourseSharing(response: CourseInfo.ShareCourse.Response)
func presentLastStep(response: CourseInfo.PresentLastStep.Response)
func presentAuthorization()

func presentWaitingState()
func dismissWaitingState()
}

final class CourseInfoPresenter: CourseInfoPresenterProtocol {
@@ -75,4 +80,25 @@ final class CourseInfoPresenter: CourseInfoPresenterProtocol {
)
self.viewController?.displayCourseSharing(viewModel: viewModel)
}

func presentWaitingState() {
self.viewController?.showBlockingLoadingIndicator()
}

func dismissWaitingState() {
self.viewController?.hideBlockingLoadingIndicator()
}

func presentLastStep(response: CourseInfo.PresentLastStep.Response) {
self.viewController?.displayLastStep(
viewModel: .init(
course: response.course,
isAdaptive: response.isAdaptive
)
)
}

func presentAuthorization() {
self.viewController?.displayAuthorization()
}
}
56 changes: 49 additions & 7 deletions Stepic/Modules/CourseInfo/CourseInfoViewController.swift
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@
import UIKit
import Pageboy
import Presentr
import SVProgressHUD

protocol CourseInfoScrollablePageViewProtocol: class {
var scrollViewDelegate: UIScrollViewDelegate? { get set }
@@ -25,6 +26,11 @@ protocol CourseInfoViewControllerProtocol: class {
func displayPersonalDeadlinesSettings(viewModel: CourseInfo.PersonalDeadlinesSettings.ViewModel)
func displayExamLesson(viewModel: CourseInfo.ShowExamLesson.ViewModel)
func displayCourseSharing(viewModel: CourseInfo.ShareCourse.ViewModel)
func displayLastStep(viewModel: CourseInfo.PresentLastStep.ViewModel)
func displayAuthorization()

func hideBlockingLoadingIndicator()
func showBlockingLoadingIndicator()
}

final class CourseInfoViewController: UIViewController {
@@ -55,6 +61,8 @@ final class CourseInfoViewController: UIViewController {

private var submodulesControllers: [UIViewController] = []

private var shouldShowDropCourseAction = false

init(interactor: CourseInfoInteractorProtocol, initialTab: CourseInfo.Tab) {
self.interactor = interactor

@@ -198,15 +206,19 @@ final class CourseInfoViewController: UIViewController {
}
)
)
alert.addAction(
UIAlertAction(
title: NSLocalizedString("DropCourse", comment: ""),
style: .destructive,
handler: { [weak self] _ in

}
if self.shouldShowDropCourseAction {
alert.addAction(
UIAlertAction(
title: NSLocalizedString("DropCourse", comment: ""),
style: .destructive,
handler: { [weak self] _ in
self?.interactor.dropCourse()
}
)
)
)
}

alert.addAction(
UIAlertAction(
title: NSLocalizedString("Cancel", comment: ""),
@@ -314,6 +326,7 @@ extension CourseInfoViewController: CourseInfoViewControllerProtocol {
switch viewModel.state {
case .result(let data):
self.courseInfoView?.configure(viewModel: data)
self.shouldShowDropCourseAction = data.isEnrolled
case .loading:
break
}
@@ -360,6 +373,31 @@ extension CourseInfoViewController: CourseInfoViewControllerProtocol {
self.present(module: viewController)
}
}

func hideBlockingLoadingIndicator() {
SVProgressHUD.dismiss()
}

func showBlockingLoadingIndicator() {
SVProgressHUD.show()
}

func displayLastStep(viewModel: CourseInfo.PresentLastStep.ViewModel) {
guard let navigationController = self.navigationController else {
return
}

LastStepRouter.continueLearning(
for: viewModel.course,
isAdaptive: viewModel.isAdaptive,
using: navigationController,
skipSyllabus: true
)
}

func displayAuthorization() {
RoutingManager.auth.routeFrom(controller: self, success: nil, cancel: nil)
}
}

extension CourseInfoViewController: UIScrollViewDelegate {
@@ -458,4 +496,8 @@ extension CourseInfoViewController: CourseInfoViewDelegate {
func courseInfoView(_ courseInfoView: CourseInfoView, requestScrollToPage index: Int) {
self.pageViewController.scrollToPage(.at(index: index), animated: true)
}

func courseInfoViewDidMainAction(_ courseInfoView: CourseInfoView) {
self.interactor.doMainCourseAction()
}
}
Original file line number Diff line number Diff line change
@@ -46,7 +46,11 @@ final class CourseInfoHeaderView: UIView {
return view
}()

private lazy var actionButton = ContinueActionButton(mode: .callToAction)
private lazy var actionButton: ContinueActionButton = {
let button = ContinueActionButton(mode: .callToAction)
button.addTarget(self, action: #selector(self.actionButtonClicked), for: .touchUpInside)
return button
}()

private lazy var coverImageView: CourseCoverImageView = {
let view = CourseCoverImageView()
@@ -95,6 +99,8 @@ final class CourseInfoHeaderView: UIView {

private lazy var statsView = CourseInfoStatsView()

var onActionButtonClick: (() -> Void)?

init(frame: CGRect = .zero, appearance: Appearance = Appearance()) {
self.appearance = appearance
super.init(frame: frame)
@@ -172,6 +178,11 @@ final class CourseInfoHeaderView: UIView {
self.backgroundView.loadImage(url: url)
self.coverImageView.loadImage(url: url)
}

@objc
private func actionButtonClicked() {
self.onActionButtonClick?()
}
}

extension CourseInfoHeaderView: ProgrammaticallyInitializableViewProtocol {
7 changes: 7 additions & 0 deletions Stepic/Modules/CourseInfo/View/CourseInfoView.swift
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ protocol CourseInfoViewDelegate: class {
func courseInfoView(_ courseInfoView: CourseInfoView, reportNewHeaderHeight height: CGFloat)
func courseInfoView(_ courseInfoView: CourseInfoView, requestScrollToPage index: Int)
func numberOfPages(in courseInfoView: CourseInfoView) -> Int
func courseInfoViewDidMainAction(_ courseInfoView: CourseInfoView)
}

extension CourseInfoView {
@@ -33,6 +34,12 @@ final class CourseInfoView: UIView {

private lazy var headerView: CourseInfoHeaderView = {
let view = CourseInfoHeaderView()
view.onActionButtonClick = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.delegate?.courseInfoViewDidMainAction(strongSelf)
}
return view
}()