mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
177 lines
8.3 KiB
Swift
177 lines
8.3 KiB
Swift
|
|
@objc(LKFriendRequestView)
|
|
final class FriendRequestView : UIView {
|
|
private let message: TSMessage
|
|
@objc weak var delegate: FriendRequestViewDelegate?
|
|
|
|
private var kind: Kind {
|
|
let isIncoming = message.interactionType() == .incomingMessage
|
|
return isIncoming ? .incoming : .outgoing
|
|
}
|
|
|
|
// MARK: Types
|
|
enum Kind : String { case incoming, outgoing }
|
|
|
|
// MARK: Components
|
|
private lazy var spacer1: UIView = {
|
|
let result = UIView()
|
|
result.set(.height, to: 12)
|
|
return result
|
|
}()
|
|
|
|
private lazy var spacer2: UIView = {
|
|
let result = UIView()
|
|
result.set(.height, to: Values.mediumSpacing)
|
|
return result
|
|
}()
|
|
|
|
private lazy var label: UILabel = {
|
|
let result = UILabel()
|
|
result.textColor = Colors.text
|
|
result.font = .systemFont(ofSize: Values.smallFontSize)
|
|
result.numberOfLines = 0
|
|
result.textAlignment = .center
|
|
result.lineBreakMode = .byWordWrapping
|
|
return result
|
|
}()
|
|
|
|
private lazy var buttonStackView: UIStackView = {
|
|
let result = UIStackView()
|
|
result.axis = .horizontal
|
|
result.spacing = Values.mediumSpacing
|
|
result.distribution = .fillEqually
|
|
return result
|
|
}()
|
|
|
|
// MARK: Lifecycle
|
|
@objc init(message: TSMessage) {
|
|
self.message = message
|
|
super.init(frame: CGRect.zero)
|
|
initialize()
|
|
}
|
|
|
|
required init?(coder: NSCoder) { fatalError("Using FriendRequestView.init(coder:) isn't allowed. Use FriendRequestView.init(message:) instead.") }
|
|
override init(frame: CGRect) { fatalError("Using FriendRequestView.init(frame:) isn't allowed. Use FriendRequestView.init(message:) instead.") }
|
|
|
|
private func initialize() {
|
|
// Set up UI
|
|
let mainStackView = UIStackView()
|
|
mainStackView.axis = .vertical
|
|
mainStackView.distribution = .fill
|
|
mainStackView.layoutMargins = UIEdgeInsets(top: 0, left: Values.largeSpacing, bottom: 0, right: Values.largeSpacing)
|
|
mainStackView.isLayoutMarginsRelativeArrangement = true
|
|
mainStackView.addArrangedSubview(spacer1)
|
|
mainStackView.addArrangedSubview(label)
|
|
switch kind {
|
|
case .incoming:
|
|
mainStackView.addArrangedSubview(spacer2)
|
|
mainStackView.addArrangedSubview(buttonStackView)
|
|
let declineButton = UIButton()
|
|
declineButton.set(.height, to: Values.mediumButtonHeight)
|
|
declineButton.layer.cornerRadius = Values.modalButtonCornerRadius
|
|
declineButton.backgroundColor = Colors.buttonBackground
|
|
declineButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize)
|
|
declineButton.setTitleColor(Colors.text, for: UIControl.State.normal)
|
|
declineButton.setTitle(NSLocalizedString("Decline", comment: ""), for: UIControl.State.normal)
|
|
declineButton.addTarget(self, action: #selector(decline), for: UIControl.Event.touchUpInside)
|
|
buttonStackView.addArrangedSubview(declineButton)
|
|
let acceptButton = UIButton()
|
|
acceptButton.set(.height, to: Values.mediumButtonHeight)
|
|
acceptButton.layer.cornerRadius = Values.modalButtonCornerRadius
|
|
acceptButton.backgroundColor = Colors.accent
|
|
acceptButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize)
|
|
acceptButton.setTitleColor(Colors.text, for: UIControl.State.normal)
|
|
acceptButton.setTitle(NSLocalizedString("Accept", comment: ""), for: UIControl.State.normal)
|
|
acceptButton.addTarget(self, action: #selector(accept), for: UIControl.Event.touchUpInside)
|
|
buttonStackView.addArrangedSubview(acceptButton)
|
|
case .outgoing: break
|
|
}
|
|
addSubview(mainStackView)
|
|
mainStackView.pin(to: self)
|
|
updateUI()
|
|
// Observe friend request status changes
|
|
NotificationCenter.default.addObserver(self, selector: #selector(handleFriendRequestStatusChangedNotification), name: .messageFriendRequestStatusChanged, object: nil)
|
|
}
|
|
|
|
deinit {
|
|
NotificationCenter.default.removeObserver(self)
|
|
}
|
|
|
|
// MARK: Updating
|
|
@objc private func handleFriendRequestStatusChangedNotification(_ notification: Notification) {
|
|
let messageID = notification.object as! String
|
|
guard messageID == message.uniqueId && TSMessage.fetch(uniqueId: messageID) != nil else { return } // It's possible for the message to be deleted at this point
|
|
message.reload()
|
|
updateUI()
|
|
}
|
|
|
|
private func updateUI() {
|
|
switch kind {
|
|
case .incoming:
|
|
guard let message = message as? TSIncomingMessage else { preconditionFailure() }
|
|
buttonStackView.isHidden = message.friendRequestStatus != .pending
|
|
spacer2.isHidden = buttonStackView.isHidden
|
|
let format: String = {
|
|
switch (message.friendRequestStatus) {
|
|
case .none, .sendingOrFailed: preconditionFailure()
|
|
case .pending: return NSLocalizedString("%@ sent you a session request", comment: "")
|
|
case .accepted: return NSLocalizedString("You've accepted %@'s session request", comment: "")
|
|
case .declined: return NSLocalizedString("You've declined %@'s session request", comment: "")
|
|
case .expired: return NSLocalizedString("%@'s session request has expired", comment: "")
|
|
default: preconditionFailure()
|
|
}
|
|
}()
|
|
let contactID = message.authorId
|
|
let displayName = DisplayNameUtilities.getPrivateChatDisplayName(for: contactID) ?? contactID
|
|
label.text = String(format: format, displayName)
|
|
case .outgoing:
|
|
guard let message = message as? TSOutgoingMessage else { preconditionFailure() }
|
|
let format: String? = {
|
|
switch (message.friendRequestStatus) {
|
|
case .none: preconditionFailure()
|
|
case .sendingOrFailed: return nil
|
|
case .pending: return NSLocalizedString("You've sent %@ a session request", comment: "")
|
|
case .accepted: return NSLocalizedString("%@ accepted your session request", comment: "")
|
|
case .declined: preconditionFailure()
|
|
case .expired: return NSLocalizedString("Your session request to %@ has expired", comment: "")
|
|
default: preconditionFailure()
|
|
}
|
|
}()
|
|
if let format = format {
|
|
let contactID = message.thread.contactIdentifier()!
|
|
let displayName = DisplayNameUtilities.getPrivateChatDisplayName(for: contactID) ?? contactID
|
|
label.text = String(format: format, displayName)
|
|
}
|
|
label.isHidden = (format == nil)
|
|
spacer1.isHidden = (label.isHidden)
|
|
}
|
|
}
|
|
|
|
// MARK: Interaction
|
|
@objc private func accept() {
|
|
guard let message = message as? TSIncomingMessage else { preconditionFailure() }
|
|
message.saveFriendRequestStatus(.accepted, with: nil)
|
|
delegate?.acceptFriendRequest(message)
|
|
}
|
|
|
|
@objc private func decline() {
|
|
guard let message = message as? TSIncomingMessage else { preconditionFailure() }
|
|
message.saveFriendRequestStatus(.declined, with: nil)
|
|
delegate?.declineFriendRequest(message)
|
|
}
|
|
|
|
// MARK: Measuring
|
|
@objc static func calculateHeight(message: TSMessage, conversationStyle: ConversationStyle) -> CGFloat {
|
|
let width = conversationStyle.contentWidth - 2 * Values.largeSpacing
|
|
let dummyFriendRequestView = FriendRequestView(message: message)
|
|
let hasTopSpacer = !dummyFriendRequestView.spacer1.isHidden
|
|
let topSpacing: CGFloat = hasTopSpacer ? 12 : 0
|
|
let hasLabel = !dummyFriendRequestView.label.isHidden
|
|
let labelHeight = hasLabel ? dummyFriendRequestView.label.sizeThatFits(CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)).height : 0
|
|
let hasButtonStackView = dummyFriendRequestView.buttonStackView.superview != nil && !dummyFriendRequestView.buttonStackView.isHidden
|
|
let buttonHeight = hasButtonStackView ? Values.mediumButtonHeight + Values.mediumSpacing : 0 // Values.mediumSpacing is the height of the spacer
|
|
let totalHeight = topSpacing + labelHeight + buttonHeight
|
|
return totalHeight.rounded(.up)
|
|
}
|
|
}
|