Skip to content
This repository has been archived by the owner on May 6, 2024. It is now read-only.

Commit

Permalink
chore: resume course button implementation for new design #1737 (#1738)
Browse files Browse the repository at this point in the history
  • Loading branch information
Muhammad Umer authored Mar 17, 2023
1 parent 5a1c318 commit 5e53bda
Show file tree
Hide file tree
Showing 18 changed files with 220 additions and 64 deletions.
2 changes: 2 additions & 0 deletions Source/CourseOutlineHeaderCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ class CourseOutlineHeaderCell: UITableViewHeaderFooterView {
containerView.addSubview(button)
containerView.addSubview(trailingImageView)
button.superview?.bringSubviewToFront(button)

containerView.backgroundColor = OEXStyles.shared().neutralWhiteT()
}

func setConstraintsForNewDesign() {
Expand Down
178 changes: 117 additions & 61 deletions Source/CourseOutlineTableSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@ class CourseOutlineTableController: UITableViewController, ScrollableDelegatePro
private let courseCard = CourseCardView(frame: .zero)
private var courseCertificateView: CourseCertificateView?
private let headerContainer = UIView()

private lazy var resumeCourseHeaderView = ResumeCourseHeaderView()
private lazy var resumeCourseView = CourseOutlineHeaderView(frame: .zero, styles: OEXStyles.shared(), titleText: Strings.resume, subtitleText: "Placeholder")
private lazy var valuePropView = UIView()

var courseVideosHeaderView: CourseVideosHeaderView?
let refreshController = PullRefreshController()

private var isResumeCourse = false
private var shouldHideTableViewHeader: Bool = false

Expand All @@ -60,7 +62,7 @@ class CourseOutlineTableController: UITableViewController, ScrollableDelegatePro

var isSectionOutline = false {
didSet {
if isSectionOutline || OEXConfig.shared().isNewDashboardEnabled {
if isSectionOutline || environment.config.isNewDashboardEnabled {
hideTableHeaderView()
}

Expand Down Expand Up @@ -127,13 +129,78 @@ class CourseOutlineTableController: UITableViewController, ScrollableDelegatePro
tableView.register(CourseUnknownTableViewCell.self, forCellReuseIdentifier: CourseUnknownTableViewCell.identifier)
tableView.register(CourseSectionTableViewCell.self, forCellReuseIdentifier: CourseSectionTableViewCell.identifier)
tableView.register(DiscussionTableViewCell.self, forCellReuseIdentifier: DiscussionTableViewCell.identifier)
configureHeaderView()

if !environment.config.isNewDashboardEnabled || courseOutlineMode != .full {
configureOldHeaderView()
}

refreshController.setupInScrollView(scrollView: tableView)

setAccessibilityIdentifiers()
}

private func configureNewHeaderView() {
headerContainer.addSubview(resumeCourseHeaderView)
tableView.tableHeaderView = headerContainer

if OEXConfig.shared().isNewDashboardEnabled {
hideTableHeaderView()
resumeCourseHeaderView.snp.makeConstraints { make in
make.top.equalTo(headerContainer)
make.bottom.equalTo(headerContainer).inset(StandardVerticalMargin * 2)
make.leading.equalTo(headerContainer).offset(StandardHorizontalMargin)
make.trailing.equalTo(headerContainer).inset(StandardHorizontalMargin)
make.height.equalTo(StandardVerticalMargin * 5)
}

tableView.setAndLayoutTableHeaderView(header: headerContainer)

UIView.animate(withDuration: 0.1) { [weak self] in
self?.view.layoutIfNeeded()
}
}

private func configureOldHeaderView() {
if courseOutlineMode == .full {
courseDateBannerView.delegate = self
headerContainer.addSubview(courseDateBannerView)
headerContainer.addSubview(courseCard)
headerContainer.addSubview(resumeCourseView)
addCertificateView()
addValuePropView()

courseDateBannerView.snp.remakeConstraints { make in
make.trailing.equalTo(headerContainer)
make.leading.equalTo(headerContainer)
make.top.equalTo(headerContainer)
make.height.equalTo(0)
}
}
if let course = enrollment?.course {
switch courseOutlineMode {
case .full:
CourseCardViewModel.onCourseOutline(course: course).apply(card: courseCard, networkManager: environment.networkManager)
break
case .video:
if let courseBlockID = courseBlockID {
let stream = courseQuerier.supportedBlockVideos(forCourseID: courseID, blockID: courseBlockID)
stream.listen(self) {[weak self] downloads in
self?.videos = downloads.value?.filter { $0.summary?.isDownloadableVideo ?? false }
self?.addBulkDownloadHeaderView(course: course, videos: self?.videos)
}
}
else {
videos = environment.interface?.downloadableVideos(of: course)
addBulkDownloadHeaderView(course: course, videos: videos)
}
break
}
refreshTableHeaderView(isResumeCourse: false)
}
}

private func addBulkDownloadHeaderView(course: OEXCourse, videos: [OEXHelperVideoDownload]?) {
courseVideosHeaderView = CourseVideosHeaderView(with: course, environment: environment, videos: videos, blockID: courseBlockID)
courseVideosHeaderView?.delegate = self
if let headerView = courseVideosHeaderView {
headerContainer.addSubview(headerView)
}
}

Expand Down Expand Up @@ -162,7 +229,7 @@ class CourseOutlineTableController: UITableViewController, ScrollableDelegatePro
}

private func shouldApplyNewStyle(_ group: CourseOutlineQuerier.BlockGroup) -> Bool {
return OEXConfig.shared().isNewDashboardEnabled && group.block.type == .Chapter && courseOutlineMode == .full
return environment.config.isNewDashboardEnabled && group.block.type == .Chapter && courseOutlineMode == .full
}

// MARK: UITableView DataSource & Delegate
Expand Down Expand Up @@ -345,55 +412,6 @@ extension CourseOutlineTableController {
}
}

private func configureHeaderView() {
if courseOutlineMode == .full {
courseDateBannerView.delegate = self
headerContainer.addSubview(courseDateBannerView)
headerContainer.addSubview(courseCard)
headerContainer.addSubview(resumeCourseView)
addCertificateView()
addValuePropView()

courseDateBannerView.snp.remakeConstraints { make in
make.trailing.equalTo(headerContainer)
make.leading.equalTo(headerContainer)
make.top.equalTo(headerContainer)
make.height.equalTo(0)
}
}
if let course = enrollment?.course {
switch courseOutlineMode {
case .full:
CourseCardViewModel.onCourseOutline(course: course).apply(card: courseCard, networkManager: environment.networkManager)
break
case .video:
if let courseBlockID = courseBlockID {
let stream = courseQuerier.supportedBlockVideos(forCourseID: courseID, blockID: courseBlockID)
stream.listen(self) {[weak self] downloads in
self?.videos = downloads.value?.filter { $0.summary?.isDownloadableVideo ?? false }
self?.addBulkDownloadHeaderView(course: course, videos: self?.videos)
}
}
else {
videos = environment.interface?.downloadableVideos(of: course)
addBulkDownloadHeaderView(course: course, videos: videos)
}
break
}
refreshTableHeaderView(isResumeCourse: false)
}
}

private func addBulkDownloadHeaderView(course: OEXCourse, videos: [OEXHelperVideoDownload]?) {
courseVideosHeaderView = CourseVideosHeaderView(with: course, environment: environment, videos: videos, blockID: courseBlockID)
courseVideosHeaderView?.delegate = self
if let headerView = courseVideosHeaderView {
headerContainer.addSubview(headerView)
}

refreshTableHeaderView(isResumeCourse: false)
}

private func allBlocksCompleted(for group: CourseOutlineQuerier.BlockGroup) -> Bool {
if courseOutlineMode == .video {
return group.block.type == .Unit ?
Expand Down Expand Up @@ -466,16 +484,37 @@ extension CourseOutlineTableController {

/// Shows the last accessed Header from the item as argument. Also, sets the relevant action if the course block exists in the course outline.
func showResumeCourse(item: ResumeCourseItem) {
if environment.config.isNewDashboardEnabled {
showResumeCourseNewDesign(item: item)
} else {
showResumeCourseOldDesign(item: item)
}
}

func showResumeCourseNewDesign(item: ResumeCourseItem) {
if !item.lastVisitedBlockID.isEmpty {
courseQuerier.blockWithID(id: item.lastVisitedBlockID).extendLifetimeUntilFirstResult { [weak self] block in
self?.configureNewHeaderView()
self?.resumeCourseHeaderView.tapAction = { [weak self] in
self?.resumeCourse(with: item)
}
} failure: { [weak self] _ in
self?.tableView.tableHeaderView = nil
}
}
}

func showResumeCourseOldDesign(item: ResumeCourseItem) {
if !item.lastVisitedBlockID.isEmpty {
courseQuerier.blockWithID(id: item.lastVisitedBlockID).extendLifetimeUntilFirstResult (success: { [weak self] block in
courseQuerier.blockWithID(id: item.lastVisitedBlockID).extendLifetimeUntilFirstResult { [weak self] block in
self?.resumeCourseView.subtitleText = block.displayName
self?.resumeCourseView.setViewButtonAction { [weak self] _ in
self?.resumeCourse(with: item)
}
self?.refreshTableHeaderView(isResumeCourse: true)
}, failure: { [weak self] _ in
} failure: { [weak self] _ in
self?.refreshTableHeaderView(isResumeCourse: false)
})
}
} else {
refreshTableHeaderView(isResumeCourse: false)
}
Expand All @@ -491,11 +530,13 @@ extension CourseOutlineTableController {
}

func showCourseDateBanner(bannerInfo: DatesBannerInfo) {
if environment.config.isNewDashboardEnabled { return }
courseDateBannerView.bannerInfo = bannerInfo
updateCourseDateBannerView(show: true)
}

func hideCourseDateBanner() {
if environment.config.isNewDashboardEnabled { return }
courseDateBannerView.bannerInfo = nil
updateCourseDateBannerView(show: false)
}
Expand Down Expand Up @@ -532,7 +573,21 @@ extension CourseOutlineTableController {
tableView.tableHeaderView = nil
return
}

if environment.config.isNewDashboardEnabled {
updateNewHeaderConstraints()
} else {
updateOldHeaderConstraints()
}

tableView.setAndLayoutTableHeaderView(header: headerContainer)
}

private func updateNewHeaderConstraints() {

}

private func updateOldHeaderConstraints() {
var constraintView: UIView = courseCard
courseCard.snp.remakeConstraints { make in
make.trailing.equalTo(headerContainer)
Expand Down Expand Up @@ -579,10 +634,11 @@ extension CourseOutlineTableController {
make.height.equalTo(height)
make.bottom.equalTo(headerContainer)
}
tableView.setAndLayoutTableHeaderView(header: headerContainer)
}

private func refreshTableHeaderView(isResumeCourse: Bool) {
if environment.config.isNewDashboardEnabled && courseOutlineMode == .full { return }

self.isResumeCourse = isResumeCourse
resumeCourseView.isHidden = !isResumeCourse

Expand Down Expand Up @@ -709,7 +765,7 @@ extension CourseOutlineTableController {

extension CourseOutlineTableController: CourseOutlineHeaderCellDelegate {
func toggleSection(section: Int) {
if OEXConfig.shared().isNewDashboardEnabled {
if environment.config.isNewDashboardEnabled {
collapsedSections = collapsedSections.symmetricDifference([section])
tableView.reloadSections([section], with: .none)
}
Expand Down
4 changes: 4 additions & 0 deletions Source/CourseSectionTableViewCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ class CourseSectionTableViewCell: SwipeableCell, CourseBlockContainerCell {
}
downloadView.addGestureRecognizer(tapGesture)
setAccessibilityIdentifiers()

if OEXConfig.shared().isNewDashboardEnabled {
content.backgroundColor = OEXStyles.shared().neutralWhiteT()
}
}

private func setAccessibilityIdentifiers() {
Expand Down
3 changes: 3 additions & 0 deletions Source/Icon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public enum Icon {
case Announcements
case ArrowUp
case ArrowDown
case ArrowForward
case Camera
case ChevronRight
case Close
Expand Down Expand Up @@ -179,6 +180,8 @@ public enum Icon {
return MaterialIconRenderer(icon: .arrowUpward)
case .ArrowDown:
return MaterialIconRenderer(icon: .arrowDownward)
case .ArrowForward:
return MaterialIconRenderer(icon: .arrowForward)
case .Account:
return MaterialIconRenderer(icon: .moreVert)
case .Camera:
Expand Down
3 changes: 0 additions & 3 deletions Source/NewCourseDashboardViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ class NewCourseDashboardViewController: UIViewController, InterfaceOrientationOv
return view
}()

private var collapsed = false
private var isAnimating = false

private lazy var courseUpgradeHelper = CourseUpgradeHelper.shared

private var pacing: String {
Expand Down
68 changes: 68 additions & 0 deletions Source/ResumeCourseHeaderView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// ResumeCourseHeaderView.swift
// edX
//
// Created by MuhammadUmer on 12/03/2023.
// Copyright © 2023 edX. All rights reserved.
//

import UIKit

class ResumeCourseHeaderView: UIView {

var tapAction: (() -> ())?

private lazy var button: UIButton = {
let button = UIButton()

let arrowImage = Icon.ArrowForward.imageWithFontSize(size: 18).image(with: OEXStyles.shared().primaryBaseColor())
let imageAttachment = NSTextAttachment()
imageAttachment.image = arrowImage

if let image = imageAttachment.image {
imageAttachment.bounds = CGRect(x: 0, y: -4, width: image.size.width, height: image.size.height)
}

let attributedImageString = NSAttributedString(attachment: imageAttachment)
let style = OEXTextStyle(weight: .bold, size: .base, color: OEXStyles.shared().primaryBaseColor())

let attributedStrings = [
style.attributedString(withText: Strings.Dashboard.resumeCourse),
NSAttributedString(string: " "),
attributedImageString,
]

let attributedTitle = NSAttributedString.joinInNaturalLayout(attributedStrings: attributedStrings)

button.setAttributedTitle(attributedTitle, for: UIControl.State())
button.backgroundColor = OEXStyles.shared().neutralWhiteT()
button.layer.borderWidth = 1
button.layer.borderColor = OEXStyles.shared().neutralXLight().cgColor
button.layer.cornerRadius = 0
button.layer.masksToBounds = true
button.oex_addAction({ [weak self] _ in
self?.tapAction?()
}, for: .touchUpInside)
return button
}()

init() {
super.init(frame: .zero)
setupViews()
setConstraints()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

private func setupViews() {
addSubview(button)
}

private func setConstraints() {
button.snp.makeConstraints { make in
make.edges.equalTo(self)
}
}
}
2 changes: 2 additions & 0 deletions Source/ar.lproj/Localizable-2.strings
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,5 @@
"DASHBOARD.GENERAL_ERROR_MESSAGE"="An error occured while loading your courses";
/* Enrolled courses loading error try again*/
"DASHBOARD.TRY_AGAIN"="Try again";
/* Course Dashboard Resume Course button title*/
"DASHBOARD.RESUME_COURSE"="Resume course";
2 changes: 2 additions & 0 deletions Source/de.lproj/Localizable-2.strings
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,5 @@
"DASHBOARD.GENERAL_ERROR_MESSAGE"="An error occured while loading your courses";
/* Enrolled courses loading error try again*/
"DASHBOARD.TRY_AGAIN"="Try again";
/* Course Dashboard Resume Course button title*/
"DASHBOARD.RESUME_COURSE"="Resume course";
Loading

0 comments on commit 5e53bda

Please sign in to comment.