Skip to content

Commit

Permalink
[MBL-1036] Block User Popup + Banner Message (#1880)
Browse files Browse the repository at this point in the history
  • Loading branch information
scottkicks committed Nov 9, 2023
1 parent 67e4562 commit 6abd404
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 20 deletions.
Expand Up @@ -10,12 +10,14 @@ private enum Layout {
}
}

final class CommentRepliesViewController: UITableViewController {
final class CommentRepliesViewController: UITableViewController, MessageBannerViewControllerPresenting {
// MARK: Properties

private let dataSource = CommentRepliesDataSource()
internal let viewModel: CommentRepliesViewModelType = CommentRepliesViewModel()

public var messageBannerViewController: MessageBannerViewController?

private lazy var commentComposer: CommentComposerView = {
let frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: Layout.Composer.originalHeight)
let view = CommentComposerView(frame: frame)
Expand Down Expand Up @@ -72,6 +74,7 @@ final class CommentRepliesViewController: UITableViewController {

self.navigationItem.title = Strings.Replies()

self.messageBannerViewController = self.configureMessageBannerViewController(on: self)
self.tableView.dataSource = self.dataSource
self.tableView.delegate = self
self.tableView.registerCellClass(CommentCell.self)
Expand Down Expand Up @@ -159,18 +162,35 @@ final class CommentRepliesViewController: UITableViewController {
self?.tableView.scrollToRow(at: indexPath, at: .top, animated: false)
}
}

self.viewModel.outputs.userBlocked
.observeForUI()
.observeValues { [weak self] success in
self?.commentComposer.isHidden = true

// Scott TODO: Use localized strings once translations can be done [mbl-1037](https://kickstarter.atlassian.net/browse/MBL-1037)
if success {
self?.messageBannerViewController?
.showBanner(with: .success, message: "This user has been successfully blocked")
} else {
self?.messageBannerViewController?
.showBanner(with: .error, message: "Your request did not go through. Try again.")
}
}
}

private func blockUser() {
// Scott TODO: present popup UI [mbl-1036](https://kickstarter.atlassian.net/browse/MBL-1036)
private func presentBlockUserAlert(username: String) {
let alert = UIAlertController
.blockUserAlert(username: username, blockUserHandler: { _ in self.viewModel.inputs.blockUser() })
self.present(alert, animated: true)
}

private func handleCommentCellHeaderTapped(in cell: UITableViewCell, _: Comment.Author) {
private func handleCommentCellHeaderTapped(in cell: UITableViewCell, _ author: Comment.Author) {
guard AppEnvironment.current.currentUser != nil, featureBlockUsersEnabled() else { return }

let actionSheet = UIAlertController
.blockUserActionSheet(
blockUserHandler: { _ in self.blockUser() },
blockUserHandler: { _ in self.presentBlockUserAlert(username: author.name) },
sourceView: cell,
isIPad: self.traitCollection.horizontalSizeClass == .regular
)
Expand Down
Expand Up @@ -14,7 +14,7 @@ private enum Layout {
}
}

internal final class CommentsViewController: UITableViewController {
internal final class CommentsViewController: UITableViewController, MessageBannerViewControllerPresenting {
// MARK: - Properties

private lazy var commentComposer: CommentComposerView = {
Expand Down Expand Up @@ -43,6 +43,8 @@ internal final class CommentsViewController: UITableViewController {
internal let viewModel: CommentsViewModelType = CommentsViewModel()
private let dataSource = CommentsDataSource()

public var messageBannerViewController: MessageBannerViewController?

// MARK: - Accessors

internal static func configuredWith(project: Project? = nil,
Expand All @@ -62,6 +64,7 @@ internal final class CommentsViewController: UITableViewController {

self.commentComposer.delegate = self

self.messageBannerViewController = self.configureMessageBannerViewController(on: self)
self.tableView.registerCellClass(CommentCell.self)
self.tableView.registerCellClass(CommentPostFailedCell.self)
self.tableView.registerCellClass(CommentRemovedCell.self)
Expand Down Expand Up @@ -177,10 +180,27 @@ internal final class CommentsViewController: UITableViewController {
.observeValues { [weak self] helpType in
self?.presentHelpWebViewController(with: helpType)
}

self.viewModel.outputs.userBlocked
.observeForUI()
.observeValues { [weak self] success in
self?.commentComposer.isHidden = true

// Scott TODO: Use localized strings once translations can be done [mbl-1037](https://kickstarter.atlassian.net/browse/MBL-1037)
if success {
self?.messageBannerViewController?
.showBanner(with: .success, message: "This user has been successfully blocked")
} else {
self?.messageBannerViewController?
.showBanner(with: .error, message: "Your request did not go through. Try again.")
}
}
}

private func blockUser() {
// Scott TODO: present popup ui
private func presentBlockUserAlert(username: String) {
let alert = UIAlertController
.blockUserAlert(username: username, blockUserHandler: { _ in self.viewModel.inputs.blockUser() })
self.present(alert, animated: true)
}

// MARK: - Actions
Expand Down Expand Up @@ -241,12 +261,12 @@ extension CommentsViewController {
// MARK: - CommentCellDelegate

extension CommentsViewController: CommentCellDelegate {
func commentCellDidTapHeader(_ cell: CommentCell, _: Comment.Author) {
func commentCellDidTapHeader(_ cell: CommentCell, _ author: Comment.Author) {
guard AppEnvironment.current.currentUser != nil, featureBlockUsersEnabled() else { return }

let actionSheet = UIAlertController
.blockUserActionSheet(
blockUserHandler: { _ in self.blockUser() },
blockUserHandler: { _ in self.presentBlockUserAlert(username: author.name) },
sourceView: cell,
isIPad: self.traitCollection.horizontalSizeClass == .regular
)
Expand Down
Expand Up @@ -9,14 +9,22 @@ public protocol MessageBannerViewControllerPresenting {
-> MessageBannerViewController?
}

public protocol MessageBannerViewControllerDelegate: AnyObject {
func messageBannerViewDidHide(type: MessageBannerType)
}

public final class MessageBannerViewController: UIViewController, NibLoading {
@IBOutlet fileprivate var backgroundView: UIView!
@IBOutlet fileprivate var iconImageView: UIImageView!
@IBOutlet fileprivate var messageLabel: UILabel!

private var bannerType: MessageBannerType?

internal var bottomConstraint: NSLayoutConstraint?
private let viewModel: MessageBannerViewModelType = MessageBannerViewModel()

weak var delegate: MessageBannerViewControllerDelegate?

struct AnimationConstants {
static let hideDuration: TimeInterval = 0.25
static let showDuration: TimeInterval = 0.3
Expand Down Expand Up @@ -88,6 +96,7 @@ public final class MessageBannerViewController: UIViewController, NibLoading {
}

public func showBanner(with type: MessageBannerType, message: String) {
self.bannerType = type
self.viewModel.inputs.update(with: (type, message))
self.viewModel.inputs.bannerViewWillShow(true)
}
Expand All @@ -99,6 +108,7 @@ public final class MessageBannerViewController: UIViewController, NibLoading {

if !isHidden {
self.view.superview?.bringSubviewToFront(self.view)
self.view.superview?.isUserInteractionEnabled = false

self.view.isHidden = isHidden

Expand Down Expand Up @@ -140,6 +150,12 @@ public final class MessageBannerViewController: UIViewController, NibLoading {
argument: self?.backgroundView
)
}
} else {
self?.view.superview?.isUserInteractionEnabled = true

if let type = self?.bannerType {
self?.delegate?.messageBannerViewDidHide(type: type)
}
}
}
)
Expand Down Expand Up @@ -206,8 +222,7 @@ extension MessageBannerViewControllerPresenting where Self: UIViewController {

parentViewController.view.addConstraints([
bottomViewBannerConstraint,
messageBannerView.leftAnchor.constraint(equalTo: parentViewController.view.leftAnchor),
messageBannerView.rightAnchor.constraint(equalTo: parentViewController.view.rightAnchor)
messageBannerView.widthAnchor.constraint(equalTo: parentViewController.view.widthAnchor)
])

return messageBannerViewController
Expand Down
Expand Up @@ -3,12 +3,14 @@ import Library
import Prelude
import UIKit

internal final class MessagesViewController: UITableViewController {
internal final class MessagesViewController: UITableViewController, MessageBannerViewControllerPresenting {
@IBOutlet fileprivate var replyBarButtonItem: UIBarButtonItem!

fileprivate let viewModel: MessagesViewModelType = MessagesViewModel()
fileprivate let dataSource = MessagesDataSource()

public var messageBannerViewController: MessageBannerViewController?

internal static func configuredWith(messageThread: MessageThread) -> MessagesViewController {
let vc = self.instantiate()
vc.viewModel.inputs.configureWith(data: .left(messageThread))
Expand All @@ -28,6 +30,8 @@ internal final class MessagesViewController: UITableViewController {
internal override func viewDidLoad() {
super.viewDidLoad()

self.messageBannerViewController = self.configureMessageBannerViewController(on: self)
self.messageBannerViewController?.delegate = self
self.tableView.rowHeight = UITableView.automaticDimension
self.tableView.dataSource = self.dataSource

Expand Down Expand Up @@ -87,6 +91,20 @@ internal final class MessagesViewController: UITableViewController {
self.viewModel.outputs.goToBacking
.observeForControllerAction()
.observeValues { [weak self] params in self?.goToBacking(with: params) }

self.viewModel.outputs.userBlocked
.observeForUI()
.observeValues { [weak self] success in

// Scott TODO: Use localized strings once translations can be done [mbl-1037](https://kickstarter.atlassian.net/browse/MBL-1037)
if success {
self?.messageBannerViewController?
.showBanner(with: .success, message: "This user has been successfully blocked")
} else {
self?.messageBannerViewController?
.showBanner(with: .error, message: "Your request did not go through. Try again.")
}
}
}

internal override func tableView(_: UITableView, estimatedHeightForRowAt _: IndexPath)
Expand Down Expand Up @@ -144,8 +162,10 @@ internal final class MessagesViewController: UITableViewController {
self.present(vc, animated: true)
}

private func blockUser() {
// Scott TODO: present popup UI [mbl-1036](https://kickstarter.atlassian.net/browse/MBL-1036)
private func presentBlockUserAlert(username: String) {
let alert = UIAlertController
.blockUserAlert(username: username, blockUserHandler: { _ in self.viewModel.inputs.blockUser() })
self.present(alert, animated: true)
}
}

Expand All @@ -170,16 +190,26 @@ extension MessagesViewController: BackingCellDelegate {
// MARK: - MessageCellDelegate

extension MessagesViewController: MessageCellDelegate {
func messageCellDidTapHeader(_ cell: MessageCell, _: User) {
func messageCellDidTapHeader(_ cell: MessageCell, _ user: User) {
guard AppEnvironment.current.currentUser != nil, featureBlockUsersEnabled() else { return }

let actionSheet = UIAlertController
.blockUserActionSheet(
blockUserHandler: { _ in self.blockUser() },
blockUserHandler: { _ in self.presentBlockUserAlert(username: user.name) },
sourceView: cell,
isIPad: self.traitCollection.horizontalSizeClass == .regular
)

self.present(actionSheet, animated: true)
}
}

// MARK: - MessageBannerViewControllerDelegate

extension MessagesViewController: MessageBannerViewControllerDelegate {
func messageBannerViewDidHide(type: MessageBannerType) {
if type == .success {
self.navigationController?.popViewController(animated: true)
}
}
}
Expand Up @@ -82,6 +82,7 @@ public final class ProjectPageViewController: UIViewController, MessageBannerVie
self.configureNavigationSelectorView()

self.messageBannerViewController = self.configureMessageBannerViewController(on: self)
self.messageBannerViewController?.delegate = self
self.tableView.registerCellClass(ProjectFAQsAskAQuestionCell.self)
self.tableView.registerCellClass(ProjectFAQsCell.self)
self.tableView.registerCellClass(ProjectFAQsEmptyStateCell.self)
Expand Down Expand Up @@ -535,6 +536,19 @@ public final class ProjectPageViewController: UIViewController, MessageBannerVie
.observeValues { _ in
// TODO: Use this flag to hide or show the Report this project label [MBL-983](https://kickstarter.atlassian.net/browse/MBL-983)
}

self.viewModel.outputs.userBlocked
.observeForUI()
.observeValues { [weak self] success in
// Scott TODO: Use localized strings once translations can be done [mbl-1037](https://kickstarter.atlassian.net/browse/MBL-1037)
if success {
self?.messageBannerViewController?
.showBanner(with: .success, message: "This user has been successfully blocked")
} else {
self?.messageBannerViewController?
.showBanner(with: .error, message: "Your request did not go through. Try again.")
}
}
}

private func prepareToPlayAudioVideoURL(audioVideoURL: URL,
Expand Down Expand Up @@ -724,8 +738,10 @@ public final class ProjectPageViewController: UIViewController, MessageBannerVie
}
}

private func blockUser() {
// Scott TODO: present popup UI [mbl-1036](https://kickstarter.atlassian.net/browse/MBL-1036)
private func presentBlockUserAlert(username: String) {
let alert = UIAlertController
.blockUserAlert(username: username, blockUserHandler: { _ in self.viewModel.inputs.blockUser() })
self.present(alert, animated: true)
}

private func goToCreatorProfile(forProject project: Project) {
Expand Down Expand Up @@ -981,7 +997,7 @@ extension ProjectPageViewController: ProjectPamphletMainCellDelegate {

let actionSheet = UIAlertController
.blockUserActionSheet(
blockUserHandler: { _ in self.blockUser() },
blockUserHandler: { _ in self.presentBlockUserAlert(username: project.creator.name) },
viewProfileHandler: { _ in self.goToCreatorProfile(forProject: project) },
sourceView: cell,
isIPad: self.traitCollection.horizontalSizeClass == .regular
Expand Down Expand Up @@ -1073,3 +1089,11 @@ extension ProjectPageViewController: PinchToZoomDelegate, OverlayViewPresenting
})
}
}

extension ProjectPageViewController: MessageBannerViewControllerDelegate {
public func messageBannerViewDidHide(type: MessageBannerType) {
if type == .success {
self.dismiss(animated: true)
}
}
}
29 changes: 29 additions & 0 deletions Library/UIAlertController.swift
Expand Up @@ -357,6 +357,35 @@ public extension UIAlertController {
return alertController
}

static func blockUserAlert(username: String,
blockUserHandler: @escaping (UIAlertAction) -> Void) -> UIAlertController {
// Scott TODO: Use localized strings once translations can be done [mbl-1037](https://kickstarter.atlassian.net/browse/MBL-1037)
let alertController = UIAlertController(
title: "Block \(username)",
message: "Blocking this user means that you won’t see their comments or content anymore. If you have saved or backed projects from this user and would like to withdraw your support, you must remove your pledge before blocking. To unblock a user in the future, please go through our Help Center.",
preferredStyle: .alert
)

// Scott TODO: Use localized strings once translations can be done [mbl-1037](https://kickstarter.atlassian.net/browse/MBL-1037)
alertController.addAction(
UIAlertAction(
title: "Block",
style: .destructive,
handler: blockUserHandler
)
)

alertController.addAction(
UIAlertAction(
title: Strings.Cancel(),
style: .cancel,
handler: nil
)
)

return alertController
}

static func blockUserActionSheet(
blockUserHandler: @escaping (UIAlertAction) -> Void,
viewProfileHandler: ((UIAlertAction) -> Void)? = nil,
Expand Down

0 comments on commit 6abd404

Please sign in to comment.