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

Add toolbar back with Texture #32

Merged
merged 1 commit into from
Apr 6, 2021
Merged
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
225 changes: 140 additions & 85 deletions Ptt/Flows/BoardFlow/Controllers/BoardViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,24 @@ protocol BoardView: BaseView {
var onPostSelect: ((BoardPost) -> Void)? { get set }
}

final class BoardViewController: ASDKViewController<ASTableNode>, FullscreenSwipeable, BoardView {
final class BoardViewController: ASDKViewController<ASDisplayNode>, FullscreenSwipeable, BoardView {

var onPostSelect: ((BoardPost) -> Void)?

private let tableNode = ASTableNode(style: .plain)
private let boardNode = BoardNode()
private var tableNode : ASTableNode {
return boardNode.tableNode
}
private var tableView : UITableView {
return tableNode.view
}
private var activityIndicator : UIActivityIndicatorView {
return boardNode.activityIndicatorNode.view as! UIActivityIndicatorView
}
private var toolbarNode : ToolbarNode {
return boardNode.bottomToolbarNode.toolbarAreaNode
}

private let apiClient: APIClientProtocol

private var boardName : String
Expand All @@ -35,15 +45,10 @@ final class BoardViewController: ASDKViewController<ASTableNode>, FullscreenSwip
private var receivedPage : Int = 0
private let cellReuseIdentifier = "BoardPostCell"

private let bottomView = UIView()
private let toolBar = UIToolbar()
private let activityIndicator = UIActivityIndicatorView()
private var bottomViewHeightConstraint : NSLayoutConstraint? = nil

init(boardName: String, apiClient: APIClientProtocol=APIClient.shared) {
self.apiClient = apiClient
self.boardName = boardName
super.init(node: tableNode)
super.init(node: boardNode)
hidesBottomBarWhenPushed = true
}

Expand All @@ -57,86 +62,31 @@ final class BoardViewController: ASDKViewController<ASTableNode>, FullscreenSwip
title = boardName
enableFullscreenSwipeBack()

tableNode.backgroundColor = GlobalAppearance.backgroundColor
tableNode.dataSource = self
tableNode.delegate = self
if #available(iOS 13.0, *) {
} else {
tableView.indicatorStyle = .white
}
tableView.separatorStyle = .none
let edgeInsetsForToolbar = UIEdgeInsets(top: 0, left: 0, bottom: toolbarNode.toolbarHeight, right: 0)
tableView.contentInset = edgeInsetsForToolbar
tableView.scrollIndicatorInsets = edgeInsetsForToolbar

if #available(iOS 10.0, *) {
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(refresh), for: .valueChanged)
tableView.refreshControl = refreshControl
} else {
let refreshItem = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(refresh))
navigationItem.rightBarButtonItem = refreshItem
}

activityIndicator.color = .lightGray
tableView.ptt_add(subviews: [activityIndicator])
NSLayoutConstraint.activate([
activityIndicator.topAnchor.constraint(equalTo: tableView.topAnchor, constant: 20.0),
activityIndicator.centerXAnchor.constraint(equalTo: tableView.centerXAnchor)
])
toolbarNode.refreshNode.addTarget(self, action: #selector(refresh), forControlEvents: .touchUpInside)
toolbarNode.searchNode.addTarget(self, action: #selector(search), forControlEvents: .touchUpInside)
toolbarNode.composeNode.addTarget(self, action: #selector(compose), forControlEvents: .touchUpInside)
toolbarNode.moreNode.addTarget(self, action: #selector(more), forControlEvents: .touchUpInside)

refresh()
}

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()

let toolBarHeight : CGFloat = 49.0
let safeAreaBottomHeight : CGFloat = {
if #available(iOS 11.0, *) {
return view.safeAreaInsets.bottom // only available after viewDidLayoutSubviews
} else {
return 0
}
}()
bottomViewHeightConstraint?.constant = toolBarHeight + safeAreaBottomHeight

// TODO:
/*
if toolBar.superview == nil {
toolBar.frame = CGRect(x: 0, y: 0, width: bottomView.frame.width, height: toolBarHeight)
toolBar.barTintColor = GlobalAppearance.backgroundColor
bottomView.ptt_add(subviews: [toolBar])
NSLayoutConstraint.activate([
toolBar.leadingAnchor.constraint(equalTo: bottomView.leadingAnchor),
toolBar.trailingAnchor.constraint(equalTo: bottomView.trailingAnchor),
toolBar.heightAnchor.constraint(equalToConstant: toolBarHeight)
])
if #available(iOS 11.0, *) {
NSLayoutConstraint.activate([
toolBar.bottomAnchor.constraint(equalTo: bottomView.safeAreaLayoutGuide.bottomAnchor)
])
} else {
NSLayoutConstraint.activate([
toolBar.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor)
])
}

let refreshItem = UIBarButtonItem(image: StyleKit.imageOfRefresh().withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(refresh))
refreshItem.accessibilityLabel = NSLocalizedString("Refresh", comment: "")
let searchItem = UIBarButtonItem(image: StyleKit.imageOfSearch().withRenderingMode(.alwaysOriginal), style: .plain, target: nil, action: nil)
searchItem.accessibilityLabel = NSLocalizedString("Search", comment: "")
let composeItem = UIBarButtonItem(image: StyleKit.imageOfCompose().withRenderingMode(.alwaysOriginal), style: .plain, target: nil, action: nil)
composeItem.accessibilityLabel = NSLocalizedString("Compose", comment: "")
let infoItem = UIBarButtonItem(image: StyleKit.imageOfMoreH().withRenderingMode(.alwaysOriginal), style: .plain, target: nil, action: nil)
infoItem.accessibilityLabel = NSLocalizedString("More actions", comment: "")
let flexible1 = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let flexible2 = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let flexible3 = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let flexible4 = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let flexible5 = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
toolBar.setItems([flexible1, refreshItem, flexible2, searchItem, flexible3, composeItem, flexible4, infoItem, flexible5], animated: false)
}
*/
}

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

Expand All @@ -145,21 +95,6 @@ final class BoardViewController: ASDKViewController<ASTableNode>, FullscreenSwip
}
}

@objc private func refresh() {
self.board = nil
self.receivedPage = 0
tableNode.reloadData()
if #available(iOS 10.0, *) {
if let refreshControl = tableView.refreshControl {
if !refreshControl.isRefreshing {
activityIndicator.startAnimating()
}
}
} else {
activityIndicator.startAnimating()
}
}

private func requestNewPost(page: Int, context: ASBatchContext) {
if self.isRequesting {
return
Expand Down Expand Up @@ -226,6 +161,32 @@ final class BoardViewController: ASDKViewController<ASTableNode>, FullscreenSwip
}
}
}

// MARK: Button actions

@objc private func refresh() {
self.board = nil
self.receivedPage = 0
tableNode.reloadData()
if #available(iOS 10.0, *) {
if let refreshControl = tableView.refreshControl {
if !refreshControl.isRefreshing {
activityIndicator.startAnimating()
}
}
} else {
activityIndicator.startAnimating()
}
}

@objc private func search() {
}

@objc private func compose() {
}

@objc private func more() {
}
}

// MARK: - ASTableDataSource
Expand Down Expand Up @@ -265,6 +226,8 @@ extension BoardViewController: ASTableDataSource {
}
}

// MARK: ASTableDelegate

extension BoardViewController: ASTableDelegate {

func shouldBatchFetch(for tableNode: ASTableNode) -> Bool {
Expand All @@ -285,6 +248,98 @@ extension BoardViewController: ASTableDelegate {
}
}

// MARK: -

private class BoardNode: ASDisplayNode {

let tableNode = ASTableNode(style: .plain)
let activityIndicatorNode = ASDisplayNode { () -> UIView in
let activityIndicator = UIActivityIndicatorView()
activityIndicator.color = .lightGray
return activityIndicator
}
let bottomToolbarNode = BottomToolbarNode()

override init() {
super.init()

automaticallyManagesSubnodes = true
tableNode.backgroundColor = GlobalAppearance.backgroundColor
}

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
let insets = UIEdgeInsets(top: .infinity,
left: 0,
bottom: 0,
right: 0)
let toolbarInsetSpec = ASInsetLayoutSpec(insets: insets, child: bottomToolbarNode)
let tableNodeSpec = ASOverlayLayoutSpec(child: tableNode, overlay: toolbarInsetSpec)
return ASOverlayLayoutSpec(child: tableNodeSpec, overlay: activityIndicatorNode)
}
}

private class BottomToolbarNode: ASDisplayNode {

private let topBorderNode = ASDisplayNode()
let toolbarAreaNode = ToolbarNode()
private let safeAreaNode = ASDisplayNode()

override init() {
super.init()

automaticallyManagesSubnodes = true
automaticallyRelayoutOnSafeAreaChanges = true

topBorderNode.backgroundColor = UIColor(red: 0.23, green: 0.23, blue: 0.23, alpha: 1.00) // #3A3A3A
topBorderNode.style.height = ASDimensionMake(0.5)
safeAreaNode.backgroundColor = GlobalAppearance.backgroundColor
}

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
let safeAreaInsets = self.safeAreaInsets
safeAreaNode.style.height = ASDimensionMake(safeAreaInsets.top + safeAreaInsets.bottom)
return ASStackLayoutSpec(direction: .vertical, spacing: 0, justifyContent: .center, alignItems: .stretch, children: [topBorderNode, toolbarAreaNode, safeAreaNode])
}
}

private class ToolbarNode: ASDisplayNode {

let refreshNode = ASButtonNode()
let searchNode = ASButtonNode()
let composeNode = ASButtonNode()
let moreNode = ASButtonNode()
let toolbarHeight : CGFloat = 49.0

override init() {
super.init()

automaticallyManagesSubnodes = true
style.height = ASDimensionMake(toolbarHeight)
backgroundColor = GlobalAppearance.backgroundColor

refreshNode.setImage(StyleKit.imageOfRefresh().withRenderingMode(.alwaysOriginal), for: .normal)
refreshNode.setImage(StyleKit.imageOfRefresh().withRenderingMode(.alwaysTemplate), for: .highlighted)
refreshNode.accessibilityLabel = NSLocalizedString("Refresh", comment: "")
searchNode.setImage(StyleKit.imageOfSearch().withRenderingMode(.alwaysOriginal), for: .normal)
searchNode.setImage(StyleKit.imageOfSearch().withRenderingMode(.alwaysTemplate), for: .highlighted)
searchNode.accessibilityLabel = NSLocalizedString("Search", comment: "")
composeNode.setImage(StyleKit.imageOfCompose().withRenderingMode(.alwaysOriginal), for: .normal)
composeNode.setImage(StyleKit.imageOfCompose().withRenderingMode(.alwaysTemplate), for: .highlighted)
composeNode.accessibilityLabel = NSLocalizedString("Compose", comment: "")
moreNode.setImage(StyleKit.imageOfMoreH().withRenderingMode(.alwaysOriginal), for: .normal)
moreNode.setImage(StyleKit.imageOfMoreH().withRenderingMode(.alwaysTemplate), for: .highlighted)
moreNode.accessibilityLabel = NSLocalizedString("More actions", comment: "")
for buttonNode in [refreshNode, searchNode, composeNode, moreNode] {
buttonNode.style.width = ASDimensionMake(toolbarHeight + 30)
buttonNode.style.height = ASDimensionMake(toolbarHeight)
}
}

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
return ASStackLayoutSpec(direction: .horizontal, spacing: 10, justifyContent: .center, alignItems: .stretch, children: [refreshNode, searchNode, composeNode, moreNode])
}
}

private class BoardCellNode: ASCellNode {

private var titleAttributes : [NSAttributedString.Key : Any] {
Expand Down