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 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
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)
}
}
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
106 changes: 82 additions & 24 deletions Source/NewCourseContentController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class NewCourseContentController: UIViewController, InterfaceOrientationOverridi

private var courseContentViewController: CourseContentPageViewController?
private var headerViewState: HeaderViewState = .expanded
private var statusBarTag = 999999

private var currentBlock: CourseBlock? {
willSet {
Expand All @@ -63,12 +64,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 +80,6 @@ class NewCourseContentController: UIViewController, InterfaceOrientationOverridi
} else {
findCourseBlockToShow()
}
setStatusBar(color: environment.styles.primaryLightColor())
}

required init?(coder aDecoder: NSCoder) {
Expand Down Expand Up @@ -107,6 +109,14 @@ class NewCourseContentController: UIViewController, InterfaceOrientationOverridi
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: false)
updateStatusBarVisibility()
}

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) { [weak self] in
self?.removeStatusBarView()
}
}

private func addSubViews() {
Expand Down Expand Up @@ -150,7 +160,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 +180,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 +205,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 +227,36 @@ class NewCourseContentController: UIViewController, InterfaceOrientationOverridi
}

override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
setStatusBar(color: environment.styles.primaryLightColor())

coordinator.animate { [weak self] _ in
self?.updateStatusBarVisibility()
}

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

private func removeStatusBarView() {
if let statusBar = UIApplication.shared.windows.filter({ $0.isKeyWindow }).first?.viewWithTag(statusBarTag) {
statusBar.removeFromSuperview()
}
}

private func updateStatusBarVisibility() {
let window = UIApplication.shared.windows.first
let topPadding = window?.safeAreaInsets.top

if currentOrientation() == .portrait {
let statusBar = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: topPadding ?? 0.0))
statusBar.backgroundColor = environment.styles.primaryLightColor()
statusBar.tag = statusBarTag
UIApplication.shared.windows.filter { $0.isKeyWindow }.first?.addSubview(statusBar)
} else {
removeStatusBarView()
}
}
}

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

if let controller = controller.viewControllers?.first as? VideoBlockViewController {
controller.orientationDelegate = self

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

Expand Down Expand Up @@ -299,6 +343,20 @@ extension NewCourseContentController {
}
}

extension NewCourseContentController: VideoBlockViewControllerOrientationDelegate {
func changeOrientation(orientation: UIInterfaceOrientation) {
if orientation == .portrait {
if headerViewState == .collapsed {
headerViewState = .animating
expandHeaderView()
}
} else if headerViewState == .expanded {
headerViewState = .animating
collapseHeaderView()
}
}
}

fileprivate extension UIStackView {
func addArrangedSubviews(_ views: [UIView]) {
views.forEach { addArrangedSubview($0) }
Expand Down
39 changes: 37 additions & 2 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,18 @@ class NewCourseDashboardViewController: UIViewController, InterfaceOrientationOv

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

if currentOrientation() == .portrait {
setStatusBar(color: environment.styles.primaryLightColor())
} else {
removeStatusBar()
}

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

private func prepareTabViewData() {
Expand Down Expand Up @@ -602,4 +628,13 @@ public extension UIViewController {
overView.backgroundColor = color
}
}

func removeStatusBar() {
DispatchQueue.main.async { [weak self] in
let tag = 123454321
if let taggedView = self?.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
}
}
}
37 changes: 29 additions & 8 deletions Source/VideoBlockViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import Foundation
import MediaPlayer
import UIKit

protocol VideoBlockViewControllerOrientationDelegate {
func changeOrientation(orientation: UIInterfaceOrientation)
}

class VideoBlockViewController : OfflineSupportViewController, CourseBlockViewController, StatusBarOverriding, InterfaceOrientationOverriding, VideoTranscriptDelegate, RatingViewControllerDelegate, VideoPlayerDelegate {

typealias Environment = DataManagerProvider & OEXInterfaceProvider & ReachabilityProvider & OEXConfigProvider & OEXRouterProvider & OEXAnalyticsProvider & OEXStylesProvider & OEXSessionProvider & NetworkManagerProvider
Expand All @@ -32,6 +36,8 @@ class VideoBlockViewController : OfflineSupportViewController, CourseBlockViewCo
private var overlayLabel: UILabel?
var shouldCelebrationAppear: Bool

var orientationDelegate: VideoBlockViewControllerOrientationDelegate?

init(environment : Environment, blockID : CourseBlockID?, courseID: String, shouldCelebrationAppear: Bool = false) {
self.blockID = blockID
self.environment = environment
Expand Down Expand Up @@ -442,16 +448,31 @@ 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 }
orientationDelegate?.changeOrientation(orientation: currentOrientation())

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)
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