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.
		
		
		
		
		
			
		
			
				
	
	
		
			210 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			210 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Swift
		
	
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
 | 
						|
 | 
						|
import UIKit
 | 
						|
import SessionUIKit
 | 
						|
import SessionUtilitiesKit
 | 
						|
import SessionSnodeKit
 | 
						|
 | 
						|
extension ContextMenuVC {
 | 
						|
    final class ActionView: UIView {
 | 
						|
        private static let iconSize: CGFloat = 16
 | 
						|
        private static let iconImageViewSize: CGFloat = 24
 | 
						|
        
 | 
						|
        private let action: Action
 | 
						|
        private let dismiss: () -> Void
 | 
						|
        private var didTouchDownInside: Bool = false
 | 
						|
        private var timer: Timer?
 | 
						|
        
 | 
						|
        // MARK: - UI
 | 
						|
        
 | 
						|
        private lazy var iconImageView: UIImageView = {
 | 
						|
            let result: UIImageView = UIImageView()
 | 
						|
            result.contentMode = .center
 | 
						|
            result.themeTintColor = action.themeColor
 | 
						|
            result.set(.width, to: ActionView.iconImageViewSize)
 | 
						|
            result.set(.height, to: ActionView.iconImageViewSize)
 | 
						|
            
 | 
						|
            return result
 | 
						|
        }()
 | 
						|
        
 | 
						|
        private lazy var titleLabel: UILabel = {
 | 
						|
            let result: UILabel = UILabel()
 | 
						|
            result.font = .systemFont(ofSize: Values.mediumFontSize)
 | 
						|
            result.themeTextColor = action.themeColor
 | 
						|
            
 | 
						|
            return result
 | 
						|
        }()
 | 
						|
        
 | 
						|
        private lazy var subtitleLabel: UILabel = {
 | 
						|
            let result: UILabel = UILabel()
 | 
						|
            result.font = .systemFont(ofSize: Values.miniFontSize)
 | 
						|
            result.themeTextColor = action.themeColor
 | 
						|
            
 | 
						|
            return result
 | 
						|
        }()
 | 
						|
        
 | 
						|
        private lazy var labelContainer: UIView = {
 | 
						|
            let result: UIView = UIView()
 | 
						|
            result.addSubview(titleLabel)
 | 
						|
            result.addSubview(subtitleLabel)
 | 
						|
            titleLabel.pin([ UIView.HorizontalEdge.leading, UIView.HorizontalEdge.trailing, UIView.VerticalEdge.top ], to: result)
 | 
						|
            subtitleLabel.pin([ UIView.HorizontalEdge.leading, UIView.HorizontalEdge.trailing, UIView.VerticalEdge.bottom ], to: result)
 | 
						|
            titleLabel.pin(.bottom, to: .top, of: subtitleLabel)
 | 
						|
            
 | 
						|
            return result
 | 
						|
        }()
 | 
						|
        
 | 
						|
        private lazy var subtitleWidthConstraint = labelContainer.set(.width, greaterThanOrEqualTo: 115)
 | 
						|
 | 
						|
        // MARK: - Lifecycle
 | 
						|
        
 | 
						|
        init(for action: Action, dismiss: @escaping () -> Void) {
 | 
						|
            self.action = action
 | 
						|
            self.dismiss = dismiss
 | 
						|
            
 | 
						|
            super.init(frame: CGRect.zero)
 | 
						|
            self.accessibilityLabel = action.accessibilityLabel
 | 
						|
            setUpViewHierarchy()
 | 
						|
        }
 | 
						|
 | 
						|
        override init(frame: CGRect) {
 | 
						|
            preconditionFailure("Use init(for:) instead.")
 | 
						|
        }
 | 
						|
 | 
						|
        required init?(coder: NSCoder) {
 | 
						|
            preconditionFailure("Use init(for:) instead.")
 | 
						|
        }
 | 
						|
 | 
						|
        private func setUpViewHierarchy() {
 | 
						|
            themeBackgroundColor = .clear
 | 
						|
            
 | 
						|
            iconImageView.image = action.icon?
 | 
						|
                .resized(to: CGSize(width: ActionView.iconSize, height: ActionView.iconSize))?
 | 
						|
                .withRenderingMode(.alwaysTemplate)
 | 
						|
            titleLabel.text = action.title
 | 
						|
            setUpSubtitle()
 | 
						|
            
 | 
						|
            // Stack view
 | 
						|
            let stackView: UIStackView = UIStackView(arrangedSubviews: [ iconImageView, labelContainer ])
 | 
						|
            stackView.axis = .horizontal
 | 
						|
            stackView.spacing = Values.smallSpacing
 | 
						|
            stackView.alignment = .center
 | 
						|
            stackView.isLayoutMarginsRelativeArrangement = true
 | 
						|
            
 | 
						|
            let smallSpacing = Values.smallSpacing
 | 
						|
            stackView.layoutMargins = UIEdgeInsets(
 | 
						|
                top: smallSpacing,
 | 
						|
                leading: smallSpacing,
 | 
						|
                bottom: smallSpacing,
 | 
						|
                trailing: Values.mediumSpacing
 | 
						|
            )
 | 
						|
            addSubview(stackView)
 | 
						|
            stackView.pin(to: self)
 | 
						|
            
 | 
						|
            // Tap gesture recognizer
 | 
						|
            let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
 | 
						|
            addGestureRecognizer(tapGestureRecognizer)
 | 
						|
        }
 | 
						|
        
 | 
						|
        private func setUpSubtitle() {
 | 
						|
            guard 
 | 
						|
                let expiresInSeconds = self.action.expirationInfo?.expiresInSeconds,
 | 
						|
                let expiresStartedAtMs = self.action.expirationInfo?.expiresStartedAtMs
 | 
						|
            else {
 | 
						|
                subtitleLabel.isHidden = true
 | 
						|
                subtitleWidthConstraint.isActive = false
 | 
						|
                return
 | 
						|
            }
 | 
						|
            
 | 
						|
            subtitleLabel.isHidden = false
 | 
						|
            subtitleWidthConstraint.isActive = true
 | 
						|
            // To prevent a negative timer
 | 
						|
            let timeToExpireInSeconds: TimeInterval =  max(0, (expiresStartedAtMs + expiresInSeconds * 1000 - Double(SnodeAPI.currentOffsetTimestampMs())) / 1000)
 | 
						|
            subtitleLabel.text = "disappearingMessagesCountdownBigMobile"
 | 
						|
                .put(key: "time_large", value: timeToExpireInSeconds.formatted(format: .twoUnits))
 | 
						|
                .localized()
 | 
						|
            
 | 
						|
            timer = Timer.scheduledTimerOnMainThread(withTimeInterval: 1, repeats: true, block: { [weak self] _ in
 | 
						|
                let timeToExpireInSeconds: TimeInterval =  (expiresStartedAtMs + expiresInSeconds * 1000 - Double(SnodeAPI.currentOffsetTimestampMs())) / 1000
 | 
						|
                if timeToExpireInSeconds <= 0 {
 | 
						|
                    self?.dismissWithTimerInvalidationIfNeeded()
 | 
						|
                } else {
 | 
						|
                    self?.subtitleLabel.text = "disappearingMessagesCountdownBigMobile"
 | 
						|
                        .put(key: "time_large", value: timeToExpireInSeconds.formatted(format: .twoUnits))
 | 
						|
                        .localized()
 | 
						|
                }
 | 
						|
            })
 | 
						|
        }
 | 
						|
        
 | 
						|
        override func removeFromSuperview() {
 | 
						|
            self.timer?.invalidate()
 | 
						|
            super.removeFromSuperview()
 | 
						|
        }
 | 
						|
        
 | 
						|
        // MARK: - Interaction
 | 
						|
        
 | 
						|
        private func dismissWithTimerInvalidationIfNeeded() {
 | 
						|
            self.timer?.invalidate()
 | 
						|
            dismiss()
 | 
						|
        }
 | 
						|
        
 | 
						|
        @objc private func handleTap() {
 | 
						|
            action.work()
 | 
						|
            dismissWithTimerInvalidationIfNeeded()
 | 
						|
        }
 | 
						|
        
 | 
						|
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
 | 
						|
            guard
 | 
						|
                isUserInteractionEnabled,
 | 
						|
                let location: CGPoint = touches.first?.location(in: self),
 | 
						|
                bounds.contains(location)
 | 
						|
            else { return }
 | 
						|
            
 | 
						|
            didTouchDownInside = true
 | 
						|
            themeBackgroundColor = .contextMenu_highlight
 | 
						|
            iconImageView.themeTintColor = .contextMenu_textHighlight
 | 
						|
            titleLabel.themeTextColor = .contextMenu_textHighlight
 | 
						|
        }
 | 
						|
 | 
						|
        override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
 | 
						|
            guard
 | 
						|
                isUserInteractionEnabled,
 | 
						|
                let location: CGPoint = touches.first?.location(in: self),
 | 
						|
                bounds.contains(location),
 | 
						|
                didTouchDownInside
 | 
						|
            else {
 | 
						|
                if didTouchDownInside {
 | 
						|
                    themeBackgroundColor = .clear
 | 
						|
                    iconImageView.themeTintColor = .contextMenu_text
 | 
						|
                    titleLabel.themeTextColor = .contextMenu_text
 | 
						|
                }
 | 
						|
                return
 | 
						|
            }
 | 
						|
            
 | 
						|
            themeBackgroundColor = .contextMenu_highlight
 | 
						|
            iconImageView.themeTintColor = .contextMenu_textHighlight
 | 
						|
            titleLabel.themeTextColor = .contextMenu_textHighlight
 | 
						|
        }
 | 
						|
 | 
						|
        override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
 | 
						|
            if didTouchDownInside {
 | 
						|
                themeBackgroundColor = .clear
 | 
						|
                iconImageView.themeTintColor = .contextMenu_text
 | 
						|
                titleLabel.themeTextColor = .contextMenu_text
 | 
						|
            }
 | 
						|
            
 | 
						|
            didTouchDownInside = false
 | 
						|
        }
 | 
						|
        
 | 
						|
        override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
 | 
						|
            if didTouchDownInside {
 | 
						|
                themeBackgroundColor = .clear
 | 
						|
                iconImageView.themeTintColor = .contextMenu_text
 | 
						|
                titleLabel.themeTextColor = .contextMenu_text
 | 
						|
            }
 | 
						|
            
 | 
						|
            didTouchDownInside = false
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |