Implement blocking/unblocking
This commit is contained in:
parent
f0ead8ac23
commit
78691c7b65
|
@ -218,6 +218,8 @@
|
|||
B80A579F23DFF1F300876683 /* NewClosedGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B80A579E23DFF1F300876683 /* NewClosedGroupVC.swift */; };
|
||||
B821494625D4D6FF009C0F2A /* URLModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B821494525D4D6FF009C0F2A /* URLModal.swift */; };
|
||||
B821494F25D4E163009C0F2A /* BodyTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B821494E25D4E163009C0F2A /* BodyTextView.swift */; };
|
||||
B82149B825D60393009C0F2A /* BlockedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82149B725D60393009C0F2A /* BlockedModal.swift */; };
|
||||
B82149C125D605C6009C0F2A /* InfoBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82149C025D605C6009C0F2A /* InfoBanner.swift */; };
|
||||
B8269D2925C7A4B400488AB4 /* InputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8269D2825C7A4B400488AB4 /* InputView.swift */; };
|
||||
B8269D3325C7A8C600488AB4 /* InputViewButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8269D3225C7A8C600488AB4 /* InputViewButton.swift */; };
|
||||
B8269D3D25C7B34D00488AB4 /* InputTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8269D3C25C7B34D00488AB4 /* InputTextView.swift */; };
|
||||
|
@ -1265,6 +1267,8 @@
|
|||
B80A579E23DFF1F300876683 /* NewClosedGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewClosedGroupVC.swift; sourceTree = "<group>"; };
|
||||
B821494525D4D6FF009C0F2A /* URLModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLModal.swift; sourceTree = "<group>"; };
|
||||
B821494E25D4E163009C0F2A /* BodyTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BodyTextView.swift; sourceTree = "<group>"; };
|
||||
B82149B725D60393009C0F2A /* BlockedModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedModal.swift; sourceTree = "<group>"; };
|
||||
B82149C025D605C6009C0F2A /* InfoBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoBanner.swift; sourceTree = "<group>"; };
|
||||
B8269D2825C7A4B400488AB4 /* InputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputView.swift; sourceTree = "<group>"; };
|
||||
B8269D3225C7A8C600488AB4 /* InputViewButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputViewButton.swift; sourceTree = "<group>"; };
|
||||
B8269D3C25C7B34D00488AB4 /* InputTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputTextView.swift; sourceTree = "<group>"; };
|
||||
|
@ -2239,6 +2243,8 @@
|
|||
B897621B25D201F7004F83B2 /* ScrollToBottomButton.swift */,
|
||||
B821494525D4D6FF009C0F2A /* URLModal.swift */,
|
||||
B821494E25D4E163009C0F2A /* BodyTextView.swift */,
|
||||
B82149B725D60393009C0F2A /* BlockedModal.swift */,
|
||||
B82149C025D605C6009C0F2A /* InfoBanner.swift */,
|
||||
);
|
||||
path = "Views & Modals";
|
||||
sourceTree = "<group>";
|
||||
|
@ -5028,6 +5034,7 @@
|
|||
34D1F0521F7E8EA30066283D /* GiphyDownloader.swift in Sources */,
|
||||
450DF2051E0D74AC003D14BE /* Platform.swift in Sources */,
|
||||
4CC613362227A00400E21A3A /* ConversationSearch.swift in Sources */,
|
||||
B82149B825D60393009C0F2A /* BlockedModal.swift in Sources */,
|
||||
B82B408C239A068800A248E7 /* RegisterVC.swift in Sources */,
|
||||
346129991FD1E4DA00532771 /* SignalApp.m in Sources */,
|
||||
3496957121A301A100DCFE74 /* OWSBackupImportJob.m in Sources */,
|
||||
|
@ -5064,6 +5071,7 @@
|
|||
C331FFF42558FF0300070591 /* PNOptionView.swift in Sources */,
|
||||
4C4AE6A1224AF35700D4AF6F /* SendMediaNavigationController.swift in Sources */,
|
||||
45F32C222057297A00A300D5 /* MediaDetailViewController.m in Sources */,
|
||||
B82149C125D605C6009C0F2A /* InfoBanner.swift in Sources */,
|
||||
C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */,
|
||||
B8B26C8F234D629C004ED98C /* MentionCandidateSelectionView.swift in Sources */,
|
||||
B8CCF63F23975CFB0091D419 /* JoinOpenGroupVC.swift in Sources */,
|
||||
|
|
|
@ -24,6 +24,15 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
|
|||
}
|
||||
|
||||
func handleSendButtonTapped() {
|
||||
if let thread = thread as? TSContactThread {
|
||||
let publicKey = thread.contactIdentifier()
|
||||
guard !OWSBlockingManager.shared().isRecipientIdBlocked(publicKey) else {
|
||||
let blockedModal = BlockedModal(publicKey: publicKey)
|
||||
blockedModal.modalPresentationStyle = .overFullScreen
|
||||
blockedModal.modalTransitionStyle = .crossDissolve
|
||||
return present(blockedModal, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
// TODO: Attachments
|
||||
let text = snInputView.text.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let thread = self.thread
|
||||
|
@ -212,4 +221,14 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
|
|||
func handleReplyButtonTapped(for viewItem: ConversationViewItem) {
|
||||
reply(viewItem)
|
||||
}
|
||||
|
||||
@objc func unblock() {
|
||||
guard let thread = thread as? TSContactThread else { return }
|
||||
let publicKey = thread.contactIdentifier()
|
||||
UIView.animate(withDuration: 0.25, animations: {
|
||||
self.blockedBanner.alpha = 0
|
||||
}, completion: { _ in
|
||||
OWSBlockingManager.shared().removeBlockedPhoneNumber(publicKey)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
// • Tapping replies
|
||||
// • Mentions
|
||||
// • Remaining send logic
|
||||
// • Blocking
|
||||
// • Subtitle
|
||||
// • Resending failed messages
|
||||
// • Tapping links in link previews
|
||||
|
@ -69,6 +68,21 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, UITableViewD
|
|||
|
||||
private lazy var scrollButton = ScrollToBottomButton(delegate: self)
|
||||
|
||||
lazy var blockedBanner: InfoBanner = {
|
||||
let name: String
|
||||
if let thread = thread as? TSContactThread {
|
||||
let publicKey = thread.contactIdentifier()
|
||||
name = OWSProfileManager.shared().profileNameForRecipient(withID: publicKey, avoidingWriteTransaction: true) ?? publicKey
|
||||
} else {
|
||||
name = "Thread"
|
||||
}
|
||||
let message = "\(name) is blocked. Unblock them?"
|
||||
let result = InfoBanner(message: message, backgroundColor: Colors.destructive)
|
||||
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(unblock))
|
||||
result.addGestureRecognizer(tapGestureRecognizer)
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: Settings
|
||||
private static let bottomInset = Values.mediumSpacing
|
||||
private static let loadMoreThreshold: CGFloat = 120
|
||||
|
@ -101,11 +115,14 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, UITableViewD
|
|||
messagesTableView.pin(to: view)
|
||||
view.addSubview(scrollButton)
|
||||
scrollButton.pin(.right, to: .right, of: view, withInset: -Values.mediumSpacing)
|
||||
// Blocked banner
|
||||
addOrRemoveBlockedBanner()
|
||||
// Notifications
|
||||
let notificationCenter = NotificationCenter.default
|
||||
notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillChangeFrameNotification(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
|
||||
notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillHideNotification(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
|
||||
notificationCenter.addObserver(self, selector: #selector(handleAudioDidFinishPlayingNotification(_:)), name: .SNAudioDidFinishPlaying, object: nil)
|
||||
notificationCenter.addObserver(self, selector: #selector(addOrRemoveBlockedBanner), name: NSNotification.Name(rawValue: kNSNotificationName_BlockListDidChange), object: nil)
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
|
@ -275,6 +292,19 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, UITableViewD
|
|||
|
||||
}
|
||||
|
||||
@objc private func addOrRemoveBlockedBanner() {
|
||||
func detach() {
|
||||
blockedBanner.removeFromSuperview()
|
||||
}
|
||||
guard let thread = thread as? TSContactThread else { return detach() }
|
||||
if OWSBlockingManager.shared().isRecipientIdBlocked(thread.contactIdentifier()) {
|
||||
view.addSubview(blockedBanner)
|
||||
blockedBanner.pin([ UIView.HorizontalEdge.left, UIView.VerticalEdge.top, UIView.HorizontalEdge.right ], to: view)
|
||||
} else {
|
||||
detach()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: General
|
||||
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
return UITableView.automaticDimension
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
|
||||
final class BlockedModal : Modal {
|
||||
private let publicKey: String
|
||||
|
||||
// MARK: Lifecycle
|
||||
init(publicKey: String) {
|
||||
self.publicKey = publicKey
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
override init(nibName: String?, bundle: Bundle?) {
|
||||
preconditionFailure("Use init(publicKey:) instead.")
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
preconditionFailure("Use init(publicKey:) instead.")
|
||||
}
|
||||
|
||||
override func populateContentView() {
|
||||
// Name
|
||||
let name = OWSProfileManager.shared().profileNameForRecipient(withID: publicKey, avoidingWriteTransaction: true) ?? publicKey
|
||||
// Title
|
||||
let titleLabel = UILabel()
|
||||
titleLabel.textColor = Colors.text
|
||||
titleLabel.font = .boldSystemFont(ofSize: Values.largeFontSize)
|
||||
titleLabel.text = "Unblock \(name)?"
|
||||
titleLabel.textAlignment = .center
|
||||
// Message
|
||||
let messageLabel = UILabel()
|
||||
messageLabel.textColor = Colors.text
|
||||
messageLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||
let message = "Are you sure you want to unblock \(name)?"
|
||||
let attributedMessage = NSMutableAttributedString(string: message)
|
||||
attributedMessage.addAttributes([ .font : UIFont.boldSystemFont(ofSize: Values.smallFontSize) ], range: (message as NSString).range(of: name))
|
||||
messageLabel.attributedText = attributedMessage
|
||||
messageLabel.numberOfLines = 0
|
||||
messageLabel.lineBreakMode = .byWordWrapping
|
||||
messageLabel.textAlignment = .center
|
||||
// Unblock button
|
||||
let unblockButton = UIButton()
|
||||
unblockButton.set(.height, to: Values.mediumButtonHeight)
|
||||
unblockButton.layer.cornerRadius = Values.modalButtonCornerRadius
|
||||
unblockButton.backgroundColor = Colors.buttonBackground
|
||||
unblockButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize)
|
||||
unblockButton.setTitleColor(Colors.text, for: UIControl.State.normal)
|
||||
unblockButton.setTitle("Unblock", for: UIControl.State.normal)
|
||||
unblockButton.addTarget(self, action: #selector(unblock), for: UIControl.Event.touchUpInside)
|
||||
// Button stack view
|
||||
let buttonStackView = UIStackView(arrangedSubviews: [ cancelButton, unblockButton ])
|
||||
buttonStackView.axis = .horizontal
|
||||
buttonStackView.spacing = Values.mediumSpacing
|
||||
buttonStackView.distribution = .fillEqually
|
||||
// Main stack view
|
||||
let mainStackView = UIStackView(arrangedSubviews: [ titleLabel, messageLabel, buttonStackView ])
|
||||
mainStackView.axis = .vertical
|
||||
mainStackView.spacing = Values.largeSpacing
|
||||
contentView.addSubview(mainStackView)
|
||||
mainStackView.pin(.leading, to: .leading, of: contentView, withInset: Values.largeSpacing)
|
||||
mainStackView.pin(.top, to: .top, of: contentView, withInset: Values.largeSpacing)
|
||||
contentView.pin(.trailing, to: .trailing, of: mainStackView, withInset: Values.largeSpacing)
|
||||
contentView.pin(.bottom, to: .bottom, of: mainStackView, withInset: Values.largeSpacing)
|
||||
}
|
||||
|
||||
// MARK: Interaction
|
||||
@objc private func unblock() {
|
||||
OWSBlockingManager.shared().removeBlockedPhoneNumber(publicKey)
|
||||
presentingViewController?.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
final class InfoBanner : UIView {
|
||||
private let message: String
|
||||
private let snBackgroundColor: UIColor
|
||||
|
||||
init(message: String, backgroundColor: UIColor) {
|
||||
self.message = message
|
||||
self.snBackgroundColor = backgroundColor
|
||||
super.init(frame: CGRect.zero)
|
||||
setUpViewHierarchy()
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
preconditionFailure("Use init(message:) instead.")
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
preconditionFailure("Use init(coder:) instead.")
|
||||
}
|
||||
|
||||
private func setUpViewHierarchy() {
|
||||
backgroundColor = snBackgroundColor
|
||||
let label = UILabel()
|
||||
label.text = message
|
||||
label.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
||||
label.textColor = .white
|
||||
label.numberOfLines = 0
|
||||
label.textAlignment = .center
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
addSubview(label)
|
||||
label.pin(to: self, withInset: Values.mediumSpacing)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue