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

Commit

Permalink
chore: add collapsible section headers (#1726)
Browse files Browse the repository at this point in the history
  • Loading branch information
Muhammad Umer committed Mar 14, 2023
1 parent 018b2f8 commit 5a1c318
Show file tree
Hide file tree
Showing 33 changed files with 513 additions and 275 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// CourseAccessErrorHelper.swift
// CourseAccessHelper.swift
// edX
//
// Created by MuhammadUmer on 05/01/2023.
Expand All @@ -13,7 +13,7 @@ enum CourseAccessErrorHelperType {
case upgradeable
}

class CourseAccessErrorHelper {
class CourseAccessHelper {
private let course: OEXCourse
private let enrollment: UserCourseEnrollment?

Expand Down
4 changes: 2 additions & 2 deletions Source/CourseDashboardAccessErrorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,15 @@ class CourseDashboardAccessErrorView: UIView {
}

private var course: OEXCourse?
private var error: CourseAccessErrorHelper?
private var error: CourseAccessHelper?

var coursePrice: String?

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

func handleCourseAccessError(environment: Environment, course: OEXCourse?, error: CourseAccessErrorHelper?) {
func handleCourseAccessError(environment: Environment, course: OEXCourse?, error: CourseAccessHelper?) {
guard let course = course else { return }

self.course = course
Expand Down
16 changes: 5 additions & 11 deletions Source/CourseDashboardHeaderView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,25 +165,19 @@ class CourseDashboardHeaderView: UIView {
}()

private lazy var accessTextStyle = OEXTextStyle(weight: .normal, size: .xSmall, color: environment.styles.neutralXLight())

private var canShowValuePropView: Bool {
guard let course = course,
let enrollment = environment.interface?.enrollmentForCourse(withID: course.course_id)
else { return false }

if let error = error, error.type == .auditExpired || error.type == .isEndDateOld {
return false
}
return enrollment.type == .audit && environment.serverConfig.valuePropEnabled
guard let enrollment = environment.interface?.enrollmentForCourse(withID: course?.course_id) else { return false }
return enrollment.isUpgradeable && environment.serverConfig.valuePropEnabled
}

private var showTabbar = false

private let environment: Environment
private let course: OEXCourse?
private let error: CourseAccessErrorHelper?
private let error: CourseAccessHelper?

init(environment: Environment, course: OEXCourse?, error: CourseAccessErrorHelper?) {
init(environment: Environment, course: OEXCourse?, error: CourseAccessHelper?) {
self.environment = environment
self.course = course
self.error = error
Expand Down
3 changes: 3 additions & 0 deletions Source/CourseGenericBlockTableViewCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ class CourseGenericBlockTableViewCell : UITableViewCell, CourseBlockContainerCel

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)

contentView.addSubview(content)

content.snp.makeConstraints { make in
make.edges.equalTo(contentView)
}

accessibilityIdentifier = "CourseGenericBlockTableViewCell:view"
content.accessibilityIdentifier = "CourseGenericBlockTableViewCell:content-view"
}
Expand Down
156 changes: 137 additions & 19 deletions Source/CourseOutlineHeaderCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,59 @@
import Foundation
import UIKit

class CourseOutlineHeaderCell : UITableViewHeaderFooterView {
protocol CourseOutlineHeaderCellDelegate: AnyObject {
func toggleSection(section: Int)
}

class CourseOutlineHeaderCell: UITableViewHeaderFooterView {

static let identifier = "CourseOutlineHeaderCellIdentifier"

let headerFontStyle = OEXTextStyle(weight: .semiBold, size: .xSmall, color: OEXStyles.shared().neutralXDark())
let headerLabel = UILabel()
let horizontalTopLine = UIView()
weak var delegate: CourseOutlineHeaderCellDelegate?

var section = 0

private var isExpanded = false
private var isCompleted = false

private let horizontalTopLine = UIView()
private let containerView = UIView()
private let iconSize = CGSize(width: 25, height: 25)
private let headerLabel = UILabel()

private lazy var headerFontStyle: OEXTextStyle = {
if OEXConfig.shared().isNewDashboardEnabled {
return OEXTextStyle(weight: .normal, size: .base, color : OEXStyles.shared().neutralBlackT())
}
return OEXTextStyle(weight: .semiBold, size: .xSmall, color: OEXStyles.shared().neutralXDark())
}()

private lazy var leadingImageButton: UIButton = {
let button = UIButton(type: .system)
button.accessibilityIdentifier = "CourseOutlineHeaderCell:trailing-button-view"
let image = Icon.CheckCircle.imageWithFontSize(size: 17)
button.setImage(image, for: .normal)
button.tintColor = OEXStyles.shared().successBase()
button.isHidden = true
return button
}()

private lazy var trailingImageView: UIImageView = {
let imageView = UIImageView()
imageView.accessibilityIdentifier = "CourseOutlineHeaderCell:trailing-image-view"
imageView.image = Icon.ExpandMore.imageWithFontSize(size: 24)
imageView.tintColor = OEXStyles.shared().neutralDark()
return imageView
}()

private lazy var button: UIButton = {
let button = UIButton()
button.accessibilityIdentifier = "CourseOutlineHeaderCell:button-view"
button.oex_addAction({ [weak self] _ in
self?.handleTap()
}, for: .touchUpInside)
return button
}()

var block: CourseBlock? {
didSet {
Expand All @@ -26,9 +73,21 @@ class CourseOutlineHeaderCell : UITableViewHeaderFooterView {

override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
addSubviews()
setStyles()
}

func setupViewsNewDesign(isExpanded: Bool, isCompleted: Bool) {
self.isExpanded = isExpanded
self.isCompleted = isCompleted

addSubviewsForNewDesign()
setConstraintsForNewDesign()
setAccessibilityIdentifiers()
backgroundView = UIView(frame: .zero)
containerView.applyBorderStyle(style: BorderStyle(cornerRadius: .Size(0), width: .Size(1), color: OEXStyles.shared().neutralDark()))
}

func setupViewsForOldDesign() {
addSubviewsForOldDesign()
}

private func setAccessibilityIdentifiers() {
Expand All @@ -40,17 +99,69 @@ class CourseOutlineHeaderCell : UITableViewHeaderFooterView {
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

//MARK: Helper Methods
private func addSubviews() {
addSubview(headerLabel)

private func addSubviewsForNewDesign() {
addSubview(containerView)
containerView.addSubview(leadingImageButton)
containerView.addSubview(headerLabel)
containerView.addSubview(button)
containerView.addSubview(trailingImageView)
button.superview?.bringSubviewToFront(button)
}

func setConstraintsForNewDesign() {
leadingImageButton.isHidden = !isCompleted

containerView.snp.remakeConstraints { make in
make.top.equalTo(self)
make.bottom.equalTo(self)
make.leading.equalTo(self).offset(StandardHorizontalMargin)
make.trailing.equalTo(self).inset(StandardHorizontalMargin)
}

leadingImageButton.snp.remakeConstraints { make in
make.centerY.equalTo(containerView)
make.leading.equalTo(containerView).offset(StandardHorizontalMargin / 2)
make.size.equalTo(iconSize)
}

trailingImageView.snp.remakeConstraints { make in
make.centerY.equalTo(containerView)
make.trailing.equalTo(containerView).inset(StandardHorizontalMargin / 2)
make.size.equalTo(iconSize)
}

headerLabel.snp.remakeConstraints { make in
make.leading.equalTo(leadingImageButton).offset(StandardHorizontalMargin * 2.15)
make.centerY.equalTo(containerView)
make.trailing.equalTo(trailingImageView.snp.leading).offset(-StandardHorizontalMargin * 2.15)
}

button.snp.remakeConstraints { make in
make.edges.equalTo(containerView)
}

rotateImageView(clockWise: isExpanded)
}

private func addSubviewsForOldDesign() {
addSubview(horizontalTopLine)
addSubview(headerLabel)
backgroundView = UIView(frame: .zero)
}

override func layoutSubviews() {
super.layoutSubviews()

if subviews.contains(headerLabel) && subviews.contains(horizontalTopLine) {
let margin = StandardHorizontalMargin - 5
headerLabel.frame = bounds.inset(by: UIEdgeInsets(top: 0, left: margin, bottom: 0, right: margin))
horizontalTopLine.frame = CGRect(x: 0, y: 0, width: bounds.size.width, height: OEXStyles.dividerSize())
}
}

private func setStyles() {
//Using CGRectZero size because the backgroundView automatically resizes.
backgroundView = UIView(frame: .zero)

horizontalTopLine.backgroundColor = OEXStyles.shared().neutralBase()
}

Expand All @@ -67,12 +178,19 @@ class CourseOutlineHeaderCell : UITableViewHeaderFooterView {
private func updateAccessibilityLabel(completion: Bool) {
headerLabel.accessibilityHint = completion ? Strings.Accessibility.completed : nil
}

// Skip autolayout for performance reasons
override func layoutSubviews() {
super.layoutSubviews()
let margin = StandardHorizontalMargin - 5
headerLabel.frame = bounds.inset(by: UIEdgeInsets.init(top: 0, left: margin, bottom: 0, right: margin))
horizontalTopLine.frame = CGRect(x: 0, y: 0, width: bounds.size.width, height: OEXStyles.dividerSize())

private func handleTap() {
delegate?.toggleSection(section: section)
}

private func rotateImageView(clockWise: Bool) {
UIView.animate(withDuration: 0.2) { [weak self] in
guard let weakSelf = self else { return }
if clockWise {
weakSelf.trailingImageView.transform = weakSelf.trailingImageView.transform.rotated(by: -(.pi * 0.999))
} else {
weakSelf.trailingImageView.transform = .identity
}
}
}
}

0 comments on commit 5a1c318

Please sign in to comment.