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.
		
		
		
		
		
			
		
			
				
	
	
		
			189 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			189 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Swift
		
	
import PromiseKit
 | 
						|
import NVActivityIndicatorView
 | 
						|
 | 
						|
final class OpenGroupSuggestionGrid : UIView, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
 | 
						|
    private let maxWidth: CGFloat
 | 
						|
    private var rooms: [OpenGroupAPIV2.Info] = [] { didSet { update() } }
 | 
						|
    private var heightConstraint: NSLayoutConstraint!
 | 
						|
    var delegate: OpenGroupSuggestionGridDelegate?
 | 
						|
    
 | 
						|
    // MARK: UI Components
 | 
						|
    private lazy var layout: UICollectionViewFlowLayout = {
 | 
						|
        let result = UICollectionViewFlowLayout()
 | 
						|
        result.minimumLineSpacing = 0
 | 
						|
        result.minimumInteritemSpacing = 0
 | 
						|
        return result
 | 
						|
    }()
 | 
						|
    
 | 
						|
    private lazy var collectionView: UICollectionView = {
 | 
						|
        let result = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
 | 
						|
        result.register(Cell.self, forCellWithReuseIdentifier: Cell.identifier)
 | 
						|
        result.backgroundColor = .clear
 | 
						|
        result.isScrollEnabled = false
 | 
						|
        result.dataSource = self
 | 
						|
        result.delegate = self
 | 
						|
        return result
 | 
						|
    }()
 | 
						|
    
 | 
						|
    private lazy var spinner: NVActivityIndicatorView = {
 | 
						|
        let result = NVActivityIndicatorView(frame: CGRect.zero, type: .circleStrokeSpin, color: Colors.text, padding: nil)
 | 
						|
        result.set(.width, to: OpenGroupSuggestionGrid.cellHeight)
 | 
						|
        result.set(.height, to: OpenGroupSuggestionGrid.cellHeight)
 | 
						|
        return result
 | 
						|
    }()
 | 
						|
    
 | 
						|
    // MARK: Settings
 | 
						|
    private static let cellHeight: CGFloat = 40
 | 
						|
    private static let separatorWidth = 1 / UIScreen.main.scale
 | 
						|
    
 | 
						|
    // MARK: Initialization
 | 
						|
    init(maxWidth: CGFloat) {
 | 
						|
        self.maxWidth = maxWidth
 | 
						|
        super.init(frame: CGRect.zero)
 | 
						|
        initialize()
 | 
						|
    }
 | 
						|
    
 | 
						|
    override init(frame: CGRect) {
 | 
						|
        preconditionFailure("Use init(maxWidth:) instead.")
 | 
						|
    }
 | 
						|
    
 | 
						|
    required init?(coder: NSCoder) {
 | 
						|
        preconditionFailure("Use init(maxWidth:) instead.")
 | 
						|
    }
 | 
						|
    
 | 
						|
    private func initialize() {
 | 
						|
        addSubview(collectionView)
 | 
						|
        collectionView.pin(to: self)
 | 
						|
        addSubview(spinner)
 | 
						|
        spinner.pin([ UIView.HorizontalEdge.left, UIView.VerticalEdge.top ], to: self)
 | 
						|
        spinner.startAnimating()
 | 
						|
        heightConstraint = set(.height, to: OpenGroupSuggestionGrid.cellHeight)
 | 
						|
        widthAnchor.constraint(greaterThanOrEqualToConstant: OpenGroupSuggestionGrid.cellHeight).isActive = true
 | 
						|
        if OpenGroupAPIV2.defaultRoomsPromise == nil {
 | 
						|
            OpenGroupAPIV2.getDefaultRoomsIfNeeded()
 | 
						|
        }
 | 
						|
        let _ = OpenGroupAPIV2.defaultRoomsPromise?.done { [weak self] rooms in
 | 
						|
            self?.rooms = rooms
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    // MARK: Updating
 | 
						|
    private func update() {
 | 
						|
        spinner.stopAnimating()
 | 
						|
        spinner.isHidden = true
 | 
						|
        let roomCount = min(rooms.count, 8) // Cap to a maximum of 8 (4 rows of 2)
 | 
						|
        let height = OpenGroupSuggestionGrid.cellHeight * ceil(CGFloat(roomCount) / 2)
 | 
						|
        heightConstraint.constant = height
 | 
						|
        collectionView.reloadData()
 | 
						|
    }
 | 
						|
    
 | 
						|
    // MARK: Layout
 | 
						|
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
 | 
						|
        let cellWidth = UIDevice.current.isIPad ? maxWidth / 4 : maxWidth / 2
 | 
						|
        return CGSize(width: cellWidth, height: OpenGroupSuggestionGrid.cellHeight)
 | 
						|
    }
 | 
						|
    
 | 
						|
    // MARK: Data Source
 | 
						|
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
 | 
						|
        return min(rooms.count, 8) // Cap to a maximum of 8 (4 rows of 2)
 | 
						|
    }
 | 
						|
    
 | 
						|
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
 | 
						|
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Cell.identifier, for: indexPath) as! Cell
 | 
						|
        cell.room = rooms[indexPath.item]
 | 
						|
        return cell
 | 
						|
    }
 | 
						|
    
 | 
						|
    // MARK: Interaction
 | 
						|
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
 | 
						|
        let room = rooms[indexPath.item]
 | 
						|
        delegate?.join(room)
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// MARK: Cell
 | 
						|
extension OpenGroupSuggestionGrid {
 | 
						|
    
 | 
						|
    fileprivate final class Cell : UICollectionViewCell {
 | 
						|
        var room: OpenGroupAPIV2.Info? { didSet { update() } }
 | 
						|
        
 | 
						|
        static let identifier = "OpenGroupSuggestionGridCell"
 | 
						|
        
 | 
						|
        private lazy var snContentView: UIView = {
 | 
						|
            let result = UIView()
 | 
						|
            result.backgroundColor = Colors.navigationBarBackground
 | 
						|
            result.set(.height, to: Cell.contentViewHeight)
 | 
						|
            result.layer.cornerRadius = Cell.contentViewCornerRadius
 | 
						|
            return result
 | 
						|
        }()
 | 
						|
        
 | 
						|
        private lazy var imageView: UIImageView = {
 | 
						|
            let result = UIImageView()
 | 
						|
            let size: CGFloat = 24
 | 
						|
            result.set(.width, to: size)
 | 
						|
            result.set(.height, to: size)
 | 
						|
            result.layer.cornerRadius = size / 2
 | 
						|
            result.clipsToBounds = true
 | 
						|
            return result
 | 
						|
        }()
 | 
						|
        
 | 
						|
        private lazy var label: UILabel = {
 | 
						|
            let result = UILabel()
 | 
						|
            result.textColor = Colors.text
 | 
						|
            result.font = .systemFont(ofSize: Values.smallFontSize)
 | 
						|
            result.lineBreakMode = .byTruncatingTail
 | 
						|
            return result
 | 
						|
        }()
 | 
						|
        
 | 
						|
        private static let contentViewInset: CGFloat = 4
 | 
						|
        private static var contentViewHeight: CGFloat { OpenGroupSuggestionGrid.cellHeight - 2 * contentViewInset }
 | 
						|
        private static var contentViewCornerRadius: CGFloat { contentViewHeight / 2 }
 | 
						|
        
 | 
						|
        override init(frame: CGRect) {
 | 
						|
            super.init(frame: frame)
 | 
						|
            setUpViewHierarchy()
 | 
						|
        }
 | 
						|
        
 | 
						|
        required init?(coder: NSCoder) {
 | 
						|
            super.init(coder: coder)
 | 
						|
            setUpViewHierarchy()
 | 
						|
        }
 | 
						|
        
 | 
						|
        private func setUpViewHierarchy() {
 | 
						|
            addSubview(snContentView)
 | 
						|
            let stackView = UIStackView(arrangedSubviews: [ imageView, label ])
 | 
						|
            stackView.axis = .horizontal
 | 
						|
            stackView.spacing = Values.smallSpacing
 | 
						|
            snContentView.addSubview(stackView)
 | 
						|
            stackView.center(.vertical, in: snContentView)
 | 
						|
            stackView.pin(.leading, to: .leading, of: snContentView, withInset: 4)
 | 
						|
            snContentView.trailingAnchor.constraint(greaterThanOrEqualTo: stackView.trailingAnchor, constant: Values.smallSpacing).isActive = true
 | 
						|
            snContentView.pin(to: self, withInset: Cell.contentViewInset)
 | 
						|
        }
 | 
						|
        
 | 
						|
        override func layoutSubviews() {
 | 
						|
            super.layoutSubviews()
 | 
						|
            let newPath = UIBezierPath(roundedRect: snContentView.bounds, cornerRadius: Cell.contentViewCornerRadius).cgPath
 | 
						|
            snContentView.layer.shadowPath = newPath
 | 
						|
            snContentView.layer.shadowColor = UIColor.black.cgColor
 | 
						|
            snContentView.layer.shadowOffset = CGSize.zero
 | 
						|
            snContentView.layer.shadowOpacity = isLightMode ? 0.2 : 0.6
 | 
						|
            snContentView.layer.shadowRadius = 2
 | 
						|
        }
 | 
						|
        
 | 
						|
        private func update() {
 | 
						|
            guard let room = room else { return }
 | 
						|
            let promise = OpenGroupAPIV2.getGroupImage(for: room.id, on: OpenGroupAPIV2.defaultServer)
 | 
						|
            imageView.image = given(promise.value) { UIImage(data: $0)! }
 | 
						|
            imageView.isHidden = (imageView.image == nil)
 | 
						|
            label.text = room.name
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// MARK: Delegate
 | 
						|
protocol OpenGroupSuggestionGridDelegate {
 | 
						|
    
 | 
						|
    func join(_ room: OpenGroupAPIV2.Info)
 | 
						|
}
 |