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.
		
		
		
		
		
			
		
			
				
	
	
		
			298 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			298 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Swift
		
	
| // Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
 | |
| 
 | |
| import UIKit
 | |
| import SessionUIKit
 | |
| import SessionMessagingKit
 | |
| import SessionUtilitiesKit
 | |
| 
 | |
| final class ConversationTitleView: UIView {
 | |
|     private static let leftInset: CGFloat = 8
 | |
|     private static let leftInsetWithCallButton: CGFloat = 54
 | |
|     
 | |
|     private var oldSize: CGSize = .zero
 | |
|     
 | |
|     override var intrinsicContentSize: CGSize {
 | |
|         return UIView.layoutFittingExpandedSize
 | |
|     }
 | |
|     
 | |
|     private lazy var labelCarouselViewWidth = labelCarouselView.set(.width, to: 185)
 | |
|     
 | |
|     public var currentLabelType: SessionLabelCarouselView.LabelType? {
 | |
|         return self.labelCarouselView.currentLabelType
 | |
|     }
 | |
| 
 | |
|     // MARK: - UI Components
 | |
|     
 | |
|     private lazy var stackViewLeadingConstraint: NSLayoutConstraint = stackView.pin(.leading, to: .leading, of: self)
 | |
|     private lazy var stackViewTrailingConstraint: NSLayoutConstraint = stackView.pin(.trailing, to: .trailing, of: self)
 | |
|     
 | |
|     private lazy var titleLabel: UILabel = {
 | |
|         let result: UILabel = UILabel()
 | |
|         result.accessibilityIdentifier = "Conversation header name"
 | |
|         result.accessibilityLabel = "Conversation header name"
 | |
|         result.isAccessibilityElement = true
 | |
|         result.font = .boldSystemFont(ofSize: Values.mediumFontSize)
 | |
|         result.themeTextColor = .textPrimary
 | |
|         result.lineBreakMode = .byTruncatingTail
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     private lazy var labelCarouselView: SessionLabelCarouselView = {
 | |
|         let result = SessionLabelCarouselView()
 | |
|         return result
 | |
|     }()
 | |
|     
 | |
|     private lazy var stackView: UIStackView = {
 | |
|         let result = UIStackView(arrangedSubviews: [ titleLabel, labelCarouselView ])
 | |
|         result.axis = .vertical
 | |
|         result.alignment = .center
 | |
|         
 | |
|         return result
 | |
|     }()
 | |
| 
 | |
|     // MARK: - Initialization
 | |
|     
 | |
|     init() {
 | |
|         super.init(frame: .zero)
 | |
|         
 | |
|         addSubview(stackView)
 | |
|         
 | |
|         stackView.pin(.top, to: .top, of: self)
 | |
|         stackViewLeadingConstraint.isActive = true
 | |
|         stackViewTrailingConstraint.isActive = true
 | |
|         stackView.pin(.bottom, to: .bottom, of: self)
 | |
|     }
 | |
| 
 | |
|     deinit {
 | |
|         NotificationCenter.default.removeObserver(self)
 | |
|     }
 | |
|     
 | |
|     required init?(coder: NSCoder) {
 | |
|         preconditionFailure("Use init() instead.")
 | |
|     }
 | |
| 
 | |
|     // MARK: - Content
 | |
|     
 | |
|     public func initialSetup(
 | |
|         with threadVariant: SessionThread.Variant,
 | |
|         isNoteToSelf: Bool
 | |
|     ) {
 | |
|         self.update(
 | |
|             with: " ",
 | |
|             isNoteToSelf: isNoteToSelf,
 | |
|             threadVariant: threadVariant,
 | |
|             mutedUntilTimestamp: nil,
 | |
|             onlyNotifyForMentions: false,
 | |
|             userCount: (threadVariant != .contact ? 0 : nil),
 | |
|             disappearingMessagesConfig: nil
 | |
|         )
 | |
|     }
 | |
|     
 | |
|     override func layoutSubviews() {
 | |
|         super.layoutSubviews()
 | |
|         
 | |
|         // There is an annoying issue where pushing seems to update the width of this
 | |
|         // view resulting in the content shifting to the right during
 | |
|         guard self.oldSize != .zero, self.oldSize != bounds.size else {
 | |
|             self.oldSize = bounds.size
 | |
|             return
 | |
|         }
 | |
|         
 | |
|         let diff: CGFloat = (bounds.size.width - oldSize.width)
 | |
|         self.stackViewTrailingConstraint.constant = -max(0, diff)
 | |
|         self.oldSize = bounds.size
 | |
|     }
 | |
|     
 | |
|     public func update(
 | |
|         with name: String,
 | |
|         isNoteToSelf: Bool,
 | |
|         threadVariant: SessionThread.Variant,
 | |
|         mutedUntilTimestamp: TimeInterval?,
 | |
|         onlyNotifyForMentions: Bool,
 | |
|         userCount: Int?,
 | |
|         disappearingMessagesConfig: DisappearingMessagesConfiguration?
 | |
|     ) {
 | |
|         guard Thread.isMainThread else {
 | |
|             DispatchQueue.main.async { [weak self] in
 | |
|                 self?.update(
 | |
|                     with: name,
 | |
|                     isNoteToSelf: isNoteToSelf,
 | |
|                     threadVariant: threadVariant,
 | |
|                     mutedUntilTimestamp: mutedUntilTimestamp,
 | |
|                     onlyNotifyForMentions: onlyNotifyForMentions,
 | |
|                     userCount: userCount,
 | |
|                     disappearingMessagesConfig: disappearingMessagesConfig
 | |
|                 )
 | |
|             }
 | |
|             return
 | |
|         }
 | |
|         
 | |
|         let shouldHaveSubtitle: Bool = (
 | |
|             Date().timeIntervalSince1970 <= (mutedUntilTimestamp ?? 0) ||
 | |
|             onlyNotifyForMentions ||
 | |
|             userCount != nil ||
 | |
|             disappearingMessagesConfig?.isEnabled == true
 | |
|         )
 | |
|         
 | |
|         self.titleLabel.text = name
 | |
|         self.titleLabel.accessibilityLabel = name
 | |
|         self.titleLabel.font = .boldSystemFont(
 | |
|             ofSize: (shouldHaveSubtitle ?
 | |
|                 Values.largeFontSize :
 | |
|                 Values.veryLargeFontSize
 | |
|             )
 | |
|         )
 | |
|         
 | |
|         ThemeManager.onThemeChange(observer: self.labelCarouselView) { [weak self] theme, _ in
 | |
|             guard let textPrimary: UIColor = theme.color(for: .textPrimary) else { return }
 | |
|             
 | |
|             var labelInfos: [SessionLabelCarouselView.LabelInfo] = []
 | |
|             
 | |
|             if Date().timeIntervalSince1970 <= (mutedUntilTimestamp ?? 0) {
 | |
|                 let notificationSettingsLabelString = NSAttributedString(
 | |
|                     string: FullConversationCell.mutePrefix,
 | |
|                     attributes: [
 | |
|                         .font: UIFont(name: "ElegantIcons", size: 8) as Any,
 | |
|                         .foregroundColor: textPrimary
 | |
|                     ]
 | |
|                 )
 | |
|                 .appending(string: "Muted")
 | |
|                 
 | |
|                 labelInfos.append(
 | |
|                     SessionLabelCarouselView.LabelInfo(
 | |
|                         attributedText: notificationSettingsLabelString,
 | |
|                         accessibility: nil, // TODO: Add accessibility
 | |
|                         type: .notificationSettings
 | |
|                     )
 | |
|                 )
 | |
|             }
 | |
|             else if onlyNotifyForMentions {
 | |
|                 let imageAttachment = NSTextAttachment()
 | |
|                 imageAttachment.image = UIImage(named: "NotifyMentions.png")?.withTint(textPrimary)
 | |
|                 imageAttachment.bounds = CGRect(
 | |
|                     x: 0,
 | |
|                     y: -2,
 | |
|                     width: Values.miniFontSize,
 | |
|                     height: Values.miniFontSize
 | |
|                 )
 | |
|                 
 | |
|                 let notificationSettingsLabelString = NSAttributedString(attachment: imageAttachment)
 | |
|                     .appending(string: "  ")
 | |
|                     .appending(string: "view_conversation_title_notify_for_mentions_only".localized())
 | |
|                 
 | |
|                 labelInfos.append(
 | |
|                     SessionLabelCarouselView.LabelInfo(
 | |
|                         attributedText: notificationSettingsLabelString,
 | |
|                         accessibility: nil, // TODO: Add accessibility
 | |
|                         type: .notificationSettings
 | |
|                     )
 | |
|                 )
 | |
|             }
 | |
|             
 | |
|             if let userCount: Int = userCount {
 | |
|                 switch threadVariant {
 | |
|                     case .contact: break
 | |
|                         
 | |
|                     case .legacyGroup, .group:
 | |
|                         labelInfos.append(
 | |
|                             SessionLabelCarouselView.LabelInfo(
 | |
|                                 attributedText: NSAttributedString(
 | |
|                                     string: "\(userCount) member\(userCount == 1 ? "" : "s")"
 | |
|                                 ),
 | |
|                                 accessibility: nil, // TODO: Add accessibility
 | |
|                                 type: .userCount
 | |
|                             )
 | |
|                         )
 | |
|                         
 | |
|                     case .community:
 | |
|                         labelInfos.append(
 | |
|                             SessionLabelCarouselView.LabelInfo(
 | |
|                                 attributedText: NSAttributedString(
 | |
|                                     string: "\(userCount) active member\(userCount == 1 ? "" : "s")"
 | |
|                                 ),
 | |
|                                 accessibility: nil, // TODO: Add accessibility
 | |
|                                 type: .userCount
 | |
|                             )
 | |
|                         )
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             if let config = disappearingMessagesConfig, config.isEnabled == true {
 | |
|                 let imageAttachment = NSTextAttachment()
 | |
|                 imageAttachment.image = UIImage(systemName: "timer")?.withTint(textPrimary)
 | |
|                 imageAttachment.bounds = CGRect(
 | |
|                     x: 0,
 | |
|                     y: -2,
 | |
|                     width: Values.miniFontSize,
 | |
|                     height: Values.miniFontSize
 | |
|                 )
 | |
|                 
 | |
|                 let disappearingMessageSettingLabelString: NSAttributedString = {
 | |
|                     guard Features.useNewDisappearingMessagesConfig else {
 | |
|                         return NSAttributedString(attachment: imageAttachment)
 | |
|                             .appending(string: " ")
 | |
|                             .appending(string: String(
 | |
|                                 format: "DISAPPERING_MESSAGES_SUMMARY_LEGACY".localized(),
 | |
|                                 floor(config.durationSeconds).formatted(format: .short)
 | |
|                             ))
 | |
|                     }
 | |
|                     
 | |
|                     return NSAttributedString(attachment: imageAttachment)
 | |
|                         .appending(string: " ")
 | |
|                         .appending(string: String(
 | |
|                             format: (config.type == .disappearAfterRead ?
 | |
|                                 "DISAPPERING_MESSAGES_SUMMARY_READ".localized() :
 | |
|                                 "DISAPPERING_MESSAGES_SUMMARY_SEND".localized()
 | |
|                             ),
 | |
|                             floor(config.durationSeconds).formatted(format: .short)
 | |
|                         ))
 | |
|                 }()
 | |
|                 
 | |
|                 labelInfos.append(
 | |
|                     SessionLabelCarouselView.LabelInfo(
 | |
|                         attributedText: disappearingMessageSettingLabelString,
 | |
|                         accessibility: Accessibility(
 | |
|                             identifier: "Disappearing messages type and time",
 | |
|                             label: "Disappearing messages type and time"
 | |
|                         ),
 | |
|                         type: .disappearingMessageSetting
 | |
|                     )
 | |
|                 )
 | |
|             }
 | |
|             
 | |
|             self?.labelCarouselView.update(
 | |
|                 with: labelInfos,
 | |
|                 labelSize: CGSize(
 | |
|                     width: self?.labelCarouselViewWidth.constant ?? 0,
 | |
|                     height: 12
 | |
|                 ),
 | |
|                 shouldAutoScroll: false
 | |
|             )
 | |
|             
 | |
|             self?.labelCarouselView.isHidden = (labelInfos.count == 0)
 | |
|         }
 | |
|         
 | |
|         // Contact threads also have the call button to compensate for
 | |
|         let shouldShowCallButton: Bool = (
 | |
|             SessionCall.isEnabled &&
 | |
|             !isNoteToSelf &&
 | |
|             threadVariant == .contact
 | |
|         )
 | |
|         self.stackViewLeadingConstraint.constant = (shouldShowCallButton ?
 | |
|             ConversationTitleView.leftInsetWithCallButton :
 | |
|             ConversationTitleView.leftInset
 | |
|         )
 | |
|         self.stackViewTrailingConstraint.constant = 0
 | |
|     }
 | |
|     
 | |
|     // MARK: - Interaction
 | |
|     
 | |
|     override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
 | |
|         if self.stackView.frame.contains(point) {
 | |
|             return self.labelCarouselView.scrollView
 | |
|         }
 | |
|         return super.hitTest(point, with: event)
 | |
|     }
 | |
| }
 |