Present action sheet when user requests info about error
Also, add mising GroupCallErrorView.swift file from last commit
This commit is contained in:
parent
85ac316064
commit
305017bcce
135
Signal/src/Calls/UserInterface/Group/GroupCallErrorView.swift
Normal file
135
Signal/src/Calls/UserInterface/Group/GroupCallErrorView.swift
Normal file
@ -0,0 +1,135 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class GroupCallErrorView: UIView {
|
||||
|
||||
// MARK: - Interface
|
||||
|
||||
var forceCompactAppearance: Bool = false {
|
||||
didSet { configure() }
|
||||
}
|
||||
|
||||
var iconImage: UIImage? {
|
||||
didSet {
|
||||
if let iconImage = iconImage {
|
||||
iconView.setTemplateImage(iconImage, tintColor: .ows_white)
|
||||
miniButton.setTemplateImage(iconImage, tintColor: .ows_white)
|
||||
} else {
|
||||
iconView.image = nil
|
||||
miniButton.setImage(nil, for: .normal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var labelText: String? {
|
||||
didSet {
|
||||
label.text = labelText
|
||||
}
|
||||
}
|
||||
|
||||
var userTapAction: ((GroupCallErrorView) -> Void)?
|
||||
|
||||
func resetConfiguration() {
|
||||
iconImage = nil
|
||||
labelText = nil
|
||||
userTapAction = nil
|
||||
}
|
||||
|
||||
// MARK: - Views
|
||||
|
||||
private let iconView: UIImageView = {
|
||||
let view = UIImageView()
|
||||
view.setTemplateImageName("error-solid-16", tintColor: .ows_white)
|
||||
return view
|
||||
}()
|
||||
|
||||
private let label: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFont.ows_dynamicTypeSubheadline
|
||||
label.textAlignment = .center
|
||||
label.textColor = .ows_white
|
||||
label.numberOfLines = 0
|
||||
return label
|
||||
}()
|
||||
|
||||
private let button: UIButton = {
|
||||
let buttonLabel = NSLocalizedString(
|
||||
"GROUP_CALL_ERROR_DETAILS",
|
||||
comment: "A button to receive more info about not seeing a participant in group call grid")
|
||||
|
||||
let button = UIButton()
|
||||
button.backgroundColor = UIColor.darkGray
|
||||
button.layer.cornerRadius = 16
|
||||
button.titleLabel?.textAlignment = .center
|
||||
button.contentEdgeInsets = UIEdgeInsets(top: 3, leading: 12, bottom: 3, trailing: 12)
|
||||
button.clipsToBounds = true
|
||||
button.setTitle(buttonLabel, for: .normal)
|
||||
button.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
|
||||
private let miniButton: UIButton = {
|
||||
let button = UIButton()
|
||||
button.contentVerticalAlignment = .fill
|
||||
button.contentHorizontalAlignment = .fill
|
||||
button.setTemplateImageName("error-solid-16", tintColor: .ows_white)
|
||||
button.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
let stackView = UIStackView(arrangedSubviews: [
|
||||
iconView,
|
||||
label,
|
||||
button,
|
||||
])
|
||||
stackView.axis = .vertical
|
||||
stackView.alignment = .center
|
||||
stackView.distribution = .fill
|
||||
|
||||
stackView.setCustomSpacing(12, after: iconView)
|
||||
stackView.setCustomSpacing(16, after: label)
|
||||
|
||||
addSubview(stackView)
|
||||
stackView.autoPinWidthToSuperviewMargins()
|
||||
stackView.autoVCenterInSuperview()
|
||||
stackView.autoPinEdge(toSuperviewMargin: .top, relation: .greaterThanOrEqual)
|
||||
stackView.autoPinEdge(toSuperviewMargin: .bottom, relation: .greaterThanOrEqual)
|
||||
|
||||
iconView.autoSetDimensions(to: CGSize(width: 24, height: 24))
|
||||
|
||||
addSubview(miniButton)
|
||||
miniButton.autoSetDimensions(to: CGSize(width: 24, height: 24))
|
||||
miniButton.autoCenterInSuperview()
|
||||
configure()
|
||||
}
|
||||
|
||||
override var bounds: CGRect {
|
||||
didSet { configure() }
|
||||
}
|
||||
|
||||
override var frame: CGRect {
|
||||
didSet { configure() }
|
||||
}
|
||||
|
||||
private func configure() {
|
||||
let isCompact = (bounds.width < 100) || (bounds.height < 100) || forceCompactAppearance
|
||||
iconView.isHidden = isCompact
|
||||
label.isHidden = isCompact
|
||||
button.isHidden = isCompact
|
||||
miniButton.isHidden = !isCompact
|
||||
}
|
||||
|
||||
@objc
|
||||
private func didTapButton() {
|
||||
userTapAction?(self)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,12 @@
|
||||
import Foundation
|
||||
import SignalRingRTC
|
||||
|
||||
protocol GroupCallMemberViewDelegate: class {
|
||||
func memberView(_: GroupCallMemberView, userRequestedInfoAboutError: GroupCallMemberView.ErrorState)
|
||||
}
|
||||
|
||||
class GroupCallMemberView: UIView {
|
||||
weak var delegate: GroupCallMemberViewDelegate?
|
||||
let noVideoView = UIView()
|
||||
|
||||
let backgroundAvatarView = UIImageView()
|
||||
@ -66,6 +71,11 @@ class GroupCallMemberView: UIView {
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
enum ErrorState {
|
||||
case blocked(SignalServiceAddress)
|
||||
case noMediaKeys(SignalServiceAddress)
|
||||
}
|
||||
}
|
||||
|
||||
class GroupCallLocalMemberView: GroupCallMemberView {
|
||||
@ -396,9 +406,14 @@ class GroupCallRemoteMemberView: GroupCallMemberView {
|
||||
|
||||
errorView.iconImage = image
|
||||
errorView.labelText = label
|
||||
errorView.userTapAction = { _ in
|
||||
// TODO
|
||||
Logger.info("Present alert controller for \(displayName)")
|
||||
errorView.userTapAction = { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
|
||||
if isBlocked {
|
||||
self.delegate?.memberView(self, userRequestedInfoAboutError: .blocked(address))
|
||||
} else {
|
||||
self.delegate?.memberView(self, userRequestedInfoAboutError: .noMediaKeys(address))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import Foundation
|
||||
import SignalRingRTC
|
||||
|
||||
class GroupCallVideoGrid: UICollectionView {
|
||||
weak var memberViewDelegate: GroupCallMemberViewDelegate?
|
||||
let layout: GroupCallVideoGridLayout
|
||||
let call: SignalCall
|
||||
init(call: SignalCall) {
|
||||
@ -65,8 +66,8 @@ extension GroupCallVideoGrid: UICollectionViewDataSource {
|
||||
return cell
|
||||
}
|
||||
|
||||
cell.setMemberViewDelegate(memberViewDelegate)
|
||||
cell.configure(call: call, device: remoteDevice)
|
||||
|
||||
return cell
|
||||
}
|
||||
}
|
||||
@ -149,6 +150,10 @@ class GroupCallVideoGridCell: UICollectionViewCell {
|
||||
func configureRemoteVideo(device: RemoteDeviceState) {
|
||||
memberView.configureRemoteVideo(device: device)
|
||||
}
|
||||
|
||||
func setMemberViewDelegate(_ delegate: GroupCallMemberViewDelegate?) {
|
||||
memberView.delegate = delegate
|
||||
}
|
||||
}
|
||||
|
||||
extension Sequence where Element: RemoteDeviceState {
|
||||
|
||||
@ -11,6 +11,7 @@ protocol GroupCallVideoOverflowDelegate: class {
|
||||
}
|
||||
|
||||
class GroupCallVideoOverflow: UICollectionView {
|
||||
weak var memberViewDelegate: GroupCallMemberViewDelegate?
|
||||
weak var overflowDelegate: GroupCallVideoOverflowDelegate?
|
||||
let call: SignalCall
|
||||
|
||||
@ -128,8 +129,8 @@ extension GroupCallVideoOverflow: UICollectionViewDataSource {
|
||||
return cell
|
||||
}
|
||||
|
||||
cell.setMemberViewDelegate(memberViewDelegate)
|
||||
cell.configure(call: call, device: remoteDevice)
|
||||
|
||||
return cell
|
||||
}
|
||||
}
|
||||
@ -186,4 +187,8 @@ class GroupCallVideoOverflowCell: UICollectionViewCell {
|
||||
func configureRemoteVideo(device: RemoteDeviceState) {
|
||||
memberView.configureRemoteVideo(device: device)
|
||||
}
|
||||
|
||||
func setMemberViewDelegate(_ delegate: GroupCallMemberViewDelegate?) {
|
||||
memberView.delegate = delegate
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,6 +47,11 @@ class GroupCallViewController: UIViewController {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
call.addObserverAndSyncState(observer: self)
|
||||
|
||||
videoGrid.memberViewDelegate = self
|
||||
videoOverflow.memberViewDelegate = self
|
||||
speakerView.delegate = self
|
||||
localMemberView.delegate = self
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@ -588,3 +593,38 @@ extension GroupCallViewController: UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension GroupCallViewController: GroupCallMemberViewDelegate {
|
||||
func memberView(_ view: GroupCallMemberView, userRequestedInfoAboutError error: GroupCallMemberView.ErrorState) {
|
||||
let title: String
|
||||
let message: String
|
||||
|
||||
switch error {
|
||||
case let .blocked(address):
|
||||
message = NSLocalizedString(
|
||||
"GROUP_CALL_BLOCKED_ALERT_MESSAGE",
|
||||
comment: "Message body for alert explaining that a group call participant is blocked")
|
||||
|
||||
let titleFormat = NSLocalizedString(
|
||||
"GROUP_CALL_BLOCKED_ALERT_TITLE_FORMAT",
|
||||
comment: "Title for alert explaining that a group call participant is blocked. Embeds {{ user's name }}")
|
||||
let displayName = contactsManager.displayName(for: address)
|
||||
title = String(format: titleFormat, [displayName])
|
||||
|
||||
case let .noMediaKeys(address):
|
||||
message = NSLocalizedString(
|
||||
"GROUP_CALL_NO_KEYS_ALERT_MESSAGE",
|
||||
comment: "Message body for alert explaining that a group call participant cannot be displayed because of missing keys")
|
||||
|
||||
let titleFormat = NSLocalizedString(
|
||||
"GROUP_CALL_NO_KEYS_ALERT_TITLE_FORMAT",
|
||||
comment: "Title for alert explaining that a group call participant cannot be displayed because of missing keys. Embeds {{ user's name }}")
|
||||
let displayName = contactsManager.displayName(for: address)
|
||||
title = String(format: titleFormat, [displayName])
|
||||
}
|
||||
|
||||
let actionSheet = ActionSheetController(title: title, message: message)
|
||||
actionSheet.addAction(ActionSheetAction(title: CommonStrings.okButton))
|
||||
presentActionSheet(actionSheet)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1522,6 +1522,12 @@
|
||||
/* Message indicating that a feature can only be used by group admins. */
|
||||
"GROUP_ADMIN_ONLY_WARNING" = "Only admins can change this option.";
|
||||
|
||||
/* Message body for alert explaining that a group call participant is blocked */
|
||||
"GROUP_CALL_BLOCKED_ALERT_MESSAGE" = "Text TBD";
|
||||
|
||||
/* Title for alert explaining that a group call participant is blocked. Embeds {{ user's name }} */
|
||||
"GROUP_CALL_BLOCKED_ALERT_TITLE_FORMAT" = "%@ is blocked";
|
||||
|
||||
/* String displayed in group call grid cell when a user is blocked. Embeds {user's name} */
|
||||
"GROUP_CALL_BLOCKED_USER_FORMAT" = "%@ is blocked and will not receive your audio or video";
|
||||
|
||||
@ -1552,6 +1558,12 @@
|
||||
/* String displayed in cell when media from a user can't be displayed in group call grid. Embeds {user's name} */
|
||||
"GROUP_CALL_MISSING_MEDIA_KEYS_FORMAT" = "Can't receive audio & video from %@";
|
||||
|
||||
/* Message body for alert explaining that a group call participant cannot be displayed because of missing keys */
|
||||
"GROUP_CALL_NO_KEYS_ALERT_MESSAGE" = "This may be because they have not verified your safety number change, there's a problem with their device, or they have blocked you.";
|
||||
|
||||
/* Title for alert explaining that a group call participant cannot be displayed because of missing keys. Embeds {{ user's name }} */
|
||||
"GROUP_CALL_NO_KEYS_ALERT_TITLE_FORMAT" = "Can't receive audio and video from %@";
|
||||
|
||||
/* Text explaining that you are the only person currently in the group call */
|
||||
"GROUP_CALL_NO_ONE_HERE" = "No one else is here";
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user