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

chore: handle landscape for new dashboard and component navigation #1769

Merged
merged 7 commits into from
Jul 10, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 5 additions & 2 deletions Source/AccessibilityCLButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ class AccessibilityCLButton: CustomPlayerButton {
}

public override func draw(_ rect: CGRect) {
let r = UIBezierPath(ovalIn: rect)
let diameter = min(rect.width, rect.height)
let circleRect = CGRect(x: rect.origin.x, y: rect.origin.y, width: diameter, height: diameter)
let path = UIBezierPath(ovalIn: circleRect)
UIColor.black.withAlphaComponent(0.65).setFill()
r.fill()
path.fill()

super.draw(rect)
}
}
7 changes: 1 addition & 6 deletions Source/CourseContentPageViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -425,18 +425,13 @@ public class CourseContentPageViewController : UIPageViewController, UIPageViewC

guard let nextController = controllerForBlock(block: block) else { return }

setPageControllers(with: [nextController], direction: direction, animated: true) { [weak self] finished in
setPageControllers(with: [nextController], direction: direction, animated: false) { [weak self] finished in
guard let weakSelf = self else { return }
weakSelf.updateTransitionState(is: false)
if weakSelf.shouldCelebrationAppear {
weakSelf.showCelebratoryModal(direction: direction, overController: nextController)
}
}

currentPageItemIndex = contentLoader.value?.currentIndex() ?? 0

updateNavigationBars()
navigationDelegate?.courseContentPageViewController(controller: self, enteredBlockWithID: cursor.current.block.blockID, parentID: cursor.current.parent)
}

func controllerForBlock(block : CourseBlock, shouldCelebrationAppear: Bool = false) -> UIViewController? {
Expand Down
5 changes: 5 additions & 0 deletions Source/CourseDashboardHeaderView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ protocol CourseDashboardHeaderViewDelegate: AnyObject {
}

enum HeaderViewState {
case initial
case animating
case expanded
case collapsed
Expand All @@ -37,6 +38,8 @@ class CourseDashboardHeaderView: UIView {
private lazy var datesBannerView = NewCourseDateBannerView()
private var bannerInfo: DatesBannerInfo? = nil

var state: HeaderViewState = .initial

private lazy var orgLabel: UILabel = {
let label = UILabel()
label.accessibilityIdentifier = "CourseDashboardHeaderView:org-label"
Expand Down Expand Up @@ -245,6 +248,8 @@ class CourseDashboardHeaderView: UIView {
make.trailing.equalTo(closeButton.snp.leading).offset(-StandardHorizontalMargin)
}

if state == .collapsed { return }

courseInfoContainerView.snp.remakeConstraints { make in
make.top.equalTo(closeButton.snp.bottom)
make.leading.equalTo(containerView).offset(StandardHorizontalMargin)
Expand Down
10 changes: 7 additions & 3 deletions Source/CutomePlayer/VideoPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ protocol VideoPlayerDelegate: AnyObject {
private var playbackLikelyToKeepUpContext = 0
class VideoPlayer: UIViewController,VideoPlayerControlsDelegate,TranscriptManagerDelegate {

typealias Environment = OEXInterfaceProvider & OEXAnalyticsProvider & OEXStylesProvider
typealias Environment = OEXInterfaceProvider & OEXAnalyticsProvider & OEXStylesProvider & OEXConfigProvider

public let environment : Environment
fileprivate var controls: VideoPlayerControls?
Expand Down Expand Up @@ -684,9 +684,13 @@ class VideoPlayer: UIViewController,VideoPlayerControlsDelegate,TranscriptManage
func setFullscreen(fullscreen: Bool, animated: Bool, with deviceOrientation: UIInterfaceOrientation, forceRotate rotate: Bool) {
if !isVisible { return }
isFullScreen = fullscreen

if fullscreen {

fullScreenContainerView = UIApplication.shared.window?.rootViewController?.view ?? UIApplication.shared.windows[0].rootViewController?.view
if environment.config.isNewComponentNavigationEnabled {
fullScreenContainerView = parent?.findParentViewController(type: NewCourseContentController.self)?.view ?? UIApplication.shared.windows.first?.rootViewController?.view
} else {
fullScreenContainerView = UIApplication.shared.window?.rootViewController?.view ?? UIApplication.shared.windows.first?.rootViewController?.view
}

if movieBackgroundView.frame == .zero {
movieBackgroundView.frame = movieBackgroundFrame
Expand Down
66 changes: 42 additions & 24 deletions Source/NewCourseContentController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,14 @@ class NewCourseContentController: UIViewController, InterfaceOrientationOverridi
private let parentID: CourseBlockID?
private let courseID: CourseBlockID
private let courseQuerier: CourseOutlineQuerier
private let courseOutlineMode: CourseOutlineMode

init(environment: Environment, blockID: CourseBlockID?, resumeCourseBlockID: CourseBlockID? = nil, parentID: CourseBlockID? = nil, courseID: CourseBlockID) {
init(environment: Environment, blockID: CourseBlockID?, resumeCourseBlockID: CourseBlockID? = nil, parentID: CourseBlockID? = nil, courseID: CourseBlockID, courseOutlineMode: CourseOutlineMode? = .full) {
self.environment = environment
self.blockID = blockID
self.parentID = parentID
self.courseID = courseID
self.courseOutlineMode = courseOutlineMode ?? .full
courseQuerier = environment.dataManager.courseDataManager.querierForCourseWithID(courseID: courseID, environment: environment)
super.init(nibName: nil, bundle: nil)

Expand All @@ -77,7 +79,6 @@ class NewCourseContentController: UIViewController, InterfaceOrientationOverridi
} else {
findCourseBlockToShow()
}
setStatusBar(color: environment.styles.primaryLightColor())
}

required init?(coder aDecoder: NSCoder) {
Expand All @@ -99,6 +100,7 @@ class NewCourseContentController: UIViewController, InterfaceOrientationOverridi
override func viewDidLoad() {
super.viewDidLoad()

setStatusBar(color: environment.styles.primaryLightColor())
addSubViews()
setupComponentView()
setupCompletedBlocksView()
Expand Down Expand Up @@ -150,7 +152,7 @@ class NewCourseContentController: UIViewController, InterfaceOrientationOverridi
let parent = courseQuerier.parentOfBlockWith(id: currentBlock.blockID).firstSuccess().value
else { return }

let courseContentViewController = CourseContentPageViewController(environment: environment, courseID: courseID, rootID: parent.blockID, initialChildID: currentBlock.blockID, forMode: .full)
let courseContentViewController = CourseContentPageViewController(environment: environment, courseID: courseID, rootID: parent.blockID, initialChildID: currentBlock.blockID, forMode: courseOutlineMode)
courseContentViewController.navigationDelegate = self

let childViewController = ForwardingNavigationController(rootViewController: courseContentViewController)
Expand All @@ -170,20 +172,15 @@ class NewCourseContentController: UIViewController, InterfaceOrientationOverridi

private func setupCompletedBlocksView() {
guard let block = currentBlock,
let section = courseQuerier.parentOfBlockWith(id: block.blockID, type: .Section).firstSuccess().value
let section = courseQuerier.parentOfBlockWith(id: block.blockID, type: .Section).firstSuccess().value,
let sectionChildren = courseQuerier.childrenOfBlockWithID(blockID: section.blockID, forMode: courseOutlineMode).value
else { return }

let childBlocks: [CourseBlock] = section.children
.compactMap { item -> CourseBlock? in
return courseQuerier.blockWithID(id: item).firstSuccess().value
}
.flatMap { item -> [CourseBlockID] in
return item.children
}
.compactMap { item -> CourseBlock? in
return courseQuerier.blockWithID(id: item).firstSuccess().value
}

let childBlocks: [CourseBlock] = sectionChildren.children.compactMap { item in
courseQuerier.childrenOfBlockWithID(blockID: item.blockID, forMode: courseOutlineMode)
.firstSuccess().value?.children ?? []
}.flatMap { $0 }

let childViews: [UIView] = childBlocks.map { block -> UIView in
let view = UIView()
view.backgroundColor = block.isCompleted ? environment.styles.accentBColor() : environment.styles.neutralDark()
Expand All @@ -200,15 +197,13 @@ class NewCourseContentController: UIViewController, InterfaceOrientationOverridi
.firstSuccess().value?.children.compactMap({ $0 }).filter({ $0.type == .Unit })
else { return }

guard let firstInCompleteBlock = childBlocks.flatMap({ $0.children })
.compactMap({ courseQuerier.blockWithID(id: $0).value })
.first(where: { !$0.isCompleted })
else {
currentBlock = courseQuerier.childrenOfBlockWithID(blockID: childBlocks.last?.blockID, forMode: .full).firstSuccess().value?.children.last
return
let blocks: [CourseBlock] = childBlocks.flatMap { block in
courseQuerier.childrenOfBlockWithID(blockID: block.blockID, forMode: courseOutlineMode).value?.children.compactMap { child in
courseQuerier.blockWithID(id: child.blockID).value
} ?? []
}
currentBlock = firstInCompleteBlock

currentBlock = blocks.first(where: { !$0.isCompleted }) ?? blocks.last
}

private func updateView() {
Expand All @@ -224,7 +219,12 @@ class NewCourseContentController: UIViewController, InterfaceOrientationOverridi
}

override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
setStatusBar(color: environment.styles.primaryLightColor())
coordinator.animate { [weak self] _ in
guard let weakSelf = self else { return }
DispatchQueue.main.async {
weakSelf.setStatusBar(color: weakSelf.environment.styles.primaryLightColor())
}
}
}
}

Expand All @@ -235,6 +235,24 @@ extension NewCourseContentController: CourseContentPageViewControllerDelegate {
if var controller = controller.viewControllers?.first as? ScrollableDelegateProvider {
controller.scrollableDelegate = self
}

// header animation is overlapping with UIPageController animation which results in crash
// calling the header animation after a delay of 1 sec to overcome the issue
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
self?.updateHeaderState(with: controller)
}
}

private func updateHeaderState(with controller: CourseContentPageViewController) {
if let controller = controller.viewControllers?.first as? VideoBlockViewController {
if currentOrientation() != .portrait {
collapseHeaderView()
} else if headerViewState == .collapsed {
collapseHeaderView()
} else if headerViewState == .expanded {
expandHeaderView()
}
}
}
}

Expand Down
68 changes: 50 additions & 18 deletions Source/NewCourseDashboardViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@ class NewCourseDashboardViewController: UIViewController, InterfaceOrientationOv
private var error: NSError?
private var courseAccessHelper: CourseAccessHelper?
private var selectedTabbarItem: TabBarItem?
private var headerViewState: HeaderViewState = .expanded

private var headerViewState: HeaderViewState = .expanded {
didSet {
headerView.state = headerViewState
}
}
private var tabBarItems: [TabBarItem] = []
private var isModalDismissable = true
private let courseStream: BackedStream<UserCourseEnrollment>
Expand Down Expand Up @@ -103,6 +108,16 @@ class NewCourseDashboardViewController: UIViewController, InterfaceOrientationOv
environment.analytics.trackScreen(withName: OEXAnalyticsScreenCourseDashboard, courseID: courseID, value: nil)
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

if headerViewState == .collapsed {
collapseHeaderView()
} else if headerViewState == .expanded {
expandHeaderView()
}
}

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.setNavigationBarHidden(false, animated: true)
Expand Down Expand Up @@ -253,7 +268,19 @@ class NewCourseDashboardViewController: UIViewController, InterfaceOrientationOv

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
setupContentView()
setStatusBar(color: environment.styles.primaryLightColor())

coordinator.animate { [weak self] _ in
guard let weakSelf = self else { return }
DispatchQueue.main.async {
weakSelf.setStatusBar(color: weakSelf.environment.styles.primaryLightColor())
}
}

if headerViewState == .collapsed {
collapseHeaderView()
} else if headerViewState == .expanded {
expandHeaderView()
}
}

private func prepareTabViewData() {
Expand Down Expand Up @@ -584,22 +611,27 @@ extension NewCourseDashboardViewController: NewCourseDashboardViewControllerDele

public extension UIViewController {
func setStatusBar(color: UIColor) {
DispatchQueue.main.async { [weak self] in
let tag = 123454321
let overView: UIView
if let taggedView = self?.view.viewWithTag(tag) {
overView = taggedView
}
else {
overView = UIView()
overView.tag = tag
self?.view.addSubview(overView)
}

let height = UIApplication.shared.window?.windowScene?.windows.first?.safeAreaInsets.top ?? 0
let frame = UIApplication.shared.window?.windowScene?.statusBarManager?.statusBarFrame ?? .zero
overView.frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width, height: height)
overView.backgroundColor = color
let tag = 123454321
let overView: UIView
if let taggedView = view.viewWithTag(tag) {
overView = taggedView
}
else {
overView = UIView()
overView.tag = tag
view.addSubview(overView)
}

let height = UIApplication.shared.window?.windowScene?.windows.first?.safeAreaInsets.top ?? 0
let frame = UIApplication.shared.window?.windowScene?.statusBarManager?.statusBarFrame ?? .zero
overView.frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width, height: height)
overView.backgroundColor = color
}

func removeStatusBar() {
let tag = 123454321
if let taggedView = view.viewWithTag(tag) {
taggedView.removeFromSuperview()
}
}
}
2 changes: 1 addition & 1 deletion Source/OEXRouter+Swift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ extension OEXRouter {

func showContainerForBlockWithID(blockID: CourseBlockID?, type: CourseBlockDisplayType, parentID: CourseBlockID?, courseID: CourseBlockID, fromController controller: UIViewController, forMode mode: CourseOutlineMode? = .full, completion: ((UIViewController) -> Void)? = nil) {
if environment.config.isNewComponentNavigationEnabled {
let contentController = NewCourseContentController(environment: environment, blockID: blockID, parentID: parentID, courseID: courseID)
let contentController = NewCourseContentController(environment: environment, blockID: blockID, parentID: parentID, courseID: courseID, courseOutlineMode: mode)
controller.navigationController?.pushViewController(contentController, animated: true, completion: completion)
} else {
showContainerForBlockWithIDOld(blockID: blockID, type: type, parentID: parentID, courseID: courseID, fromController: controller, forMode: mode, completion: completion)
Expand Down
10 changes: 10 additions & 0 deletions Source/UIViewController+CommonAdditions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,14 @@ extension UIViewController {
}
navigationItem.leftBarButtonItem = backItem
}

func findParentViewController<T: UIViewController>(type: T.Type) -> T? {
if let parentViewController = self.parent as? T {
return parentViewController
} else if let parentViewController = self.parent {
return parentViewController.findParentViewController(type: type)
} else {
return nil
}
}
}
35 changes: 24 additions & 11 deletions Source/VideoBlockViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class VideoBlockViewController : OfflineSupportViewController, CourseBlockViewCo
private var playOverlayButton: UIButton?
private var overlayLabel: UILabel?
var shouldCelebrationAppear: Bool

init(environment : Environment, blockID : CourseBlockID?, courseID: String, shouldCelebrationAppear: Bool = false) {
self.blockID = blockID
self.environment = environment
Expand Down Expand Up @@ -442,16 +442,29 @@ class VideoBlockViewController : OfflineSupportViewController, CourseBlockViewCo
}
}

// willTransition only called in case of iPhone because iPhone has regular and compact vertical classes.
// This method is specially for iPad
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
guard UIDevice.current.userInterfaceIdiom == .pad else { return }

if videoPlayer.isFullScreen {
videoPlayer.setFullscreen(fullscreen: !UIDevice.current.orientation.isPortrait, animated: true, with: currentOrientation(), forceRotate: false)
}
else if UIDevice.current.orientation.isLandscape {
videoPlayer.setFullscreen(fullscreen: true, animated: true, with: currentOrientation(), forceRotate: false)
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.current.userInterfaceIdiom == .pad {
if videoPlayer.isFullScreen {
videoPlayer.setFullscreen(fullscreen: !UIDevice.current.orientation.isPortrait, animated: true, with: currentOrientation(), forceRotate: false)
} else if UIDevice.current.orientation.isLandscape {
videoPlayer.setFullscreen(fullscreen: true, animated: true, with: currentOrientation(), forceRotate: false)
}
} else {
DispatchQueue.main.async { [weak self] in
if let weakSelf = self {
if weakSelf.chromeCastManager.isMiniPlayerAdded { return }

if weakSelf.videoPlayer.isFullScreen {
if UITraitCollection.current.verticalSizeClass == .regular {
weakSelf.videoPlayer.setFullscreen(fullscreen: false, animated: true, with: weakSelf.currentOrientation(), forceRotate: false)
} else {
weakSelf.videoPlayer.setFullscreen(fullscreen: true, animated: true, with: weakSelf.currentOrientation(), forceRotate: false)
}
} else if UITraitCollection.current.verticalSizeClass == .compact && !weakSelf.shouldCelebrationAppear {
weakSelf.videoPlayer.setFullscreen(fullscreen: true, animated: true, with: weakSelf.currentOrientation(), forceRotate: false)
}
}
}
}
}

Expand Down