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

[MBL-1036] Block User Popup + Banner Message #1880

Merged
merged 14 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
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

if success {
self?.messageBannerViewController?
.showBanner(with: .success, message: "This user has been successfully blocked")
self?.view.isUserInteractionEnabled = false
scottkicks marked this conversation as resolved.
Show resolved Hide resolved
} 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
Original file line number Diff line number Diff line change
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,26 @@ 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

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 +260,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
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,22 @@ public protocol MessageBannerViewControllerPresenting {
-> MessageBannerViewController?
}

public protocol MessageBannerViewControllerDelegate: AnyObject {
func messageBannerViewDidHide(type: MessageBannerType)
scottkicks marked this conversation as resolved.
Show resolved Hide resolved
}

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you want the message banner class to be responsible for turning on and off user interaction? I'm okay with that (as long as that works for all current call sites), but then the corresponding lines of code in the individual classes need to be deleted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep - It does work for the other call sites. Good catch. I thought I had gotten them all. Will remove it from the last 2 classes.


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
Original file line number Diff line number Diff line change
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,19 @@ 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

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.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Add a TODO here and for the other banner messages to update them once translated strings are ready

}
}
}

internal override func tableView(_: UITableView, estimatedHeightForRowAt _: IndexPath)
Expand Down Expand Up @@ -144,8 +161,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 +189,26 @@ extension MessagesViewController: BackingCellDelegate {
// MARK: - MessageCellDelegate

extension MessagesViewController: MessageCellDelegate {
func messageCellDidTapHeader(_ cell: MessageCell, _: User) {
func messageCellDidTapHeader(_ cell: MessageCell, _ author: User) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I don't think author is the right name in this context - just user would be better

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
)

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

// MARK: - MessageBannerViewControllerDelegate

extension MessagesViewController: MessageBannerViewControllerDelegate {
func messageBannerViewDidHide(type: MessageBannerType) {
if type == .success {
self.navigationController?.popViewController(animated: true)
}
}
}
Original file line number Diff line number Diff line change
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
if success {
self?.messageBannerViewController?
.showBanner(with: .success, message: "This user has been successfully blocked")
self?.view.isUserInteractionEnabled = false
} 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
Original file line number Diff line number Diff line change
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