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.
		
		
		
		
		
			
		
			
				
	
	
		
			185 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			185 lines
		
	
	
		
			7.1 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
 | |
|         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 {
 | |
|         return CGSize(width: maxWidth / 2, 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: Values.smallSpacing)
 | |
|             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 = 0.2
 | |
|             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)
 | |
| }
 |