<commit_before>//
//  CollectionViewBase.swift
//  Reactant
//
//  Created by <NAME> on 21.11.16.
//  Copyright © 2016 Brightify. All rights reserved.
//

import RxSwift

open class CollectionViewBase<MODEL, ACTION>: ViewBase<CollectionViewState<MODEL>, ACTION>, ReactantCollectionView, UICollectionViewDelegate {
    
    open var edgesForExtendedLayout: UIRectEdge {
        return .all
    }
    
    open override var configuration: Configuration {
        didSet {
            configuration.get(valueFor: Properties.Style.CollectionView.collectionView)(self)
            
            configurationChangeTime = clock()
            setNeedsLayout()
        }
    }
    
    public let collectionView: UICollectionView
    #if os(iOS)
    public let refreshControl: UIRefreshControl?
    #endif
    public let emptyLabel = UILabel()
    public let loadingIndicator = UIActivityIndicatorView()
    
    private let items = PublishSubject<[MODEL]>()
    // Optimization that prevents configuration reloading each time cell is dequeued.
    private var configurationChangeTime: clock_t = clock()

    public init(layout: UICollectionViewLayout, reloadable: Bool = true) {
        self.collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
        #if os(iOS)
        self.refreshControl = reloadable ? UIRefreshControl() : nil
        #endif
        
        super.init()
    }
    
    open override func loadView() {
        children(
            collectionView,
            emptyLabel,
            loadingIndicator
        )
        #if os(iOS)
        if let refreshControl = refreshControl {
            collectionView.children(
                refreshControl
            )
        }
        #endif
        
        loadingIndicator.hidesWhenStopped = true
        
        collectionView.backgroundColor = .clear
        collectionView.rx.setDelegate(self).disposed(by: lifetimeDisposeBag)
    }
    
    open override func setupConstraints() {
        collectionView.snp.makeConstraints { make in
            make.edges.equalTo(self)
        }
        
        emptyLabel.snp.makeConstraints { make in
            make.center.equalTo(self)
        }
        
        loadingIndicator.snp.makeConstraints { make in
            make.center.equalTo(self)
        }
    }
    
    open override func afterInit() {
        collectionView.rx.itemSelected
            .subscribe(onNext: { [collectionView] in
                collectionView.deselectItem(at: $0, animated: true)
            })
            .disposed(by: lifetimeDisposeBag)

        bind(items: items)
    }

    open func bind(items: Observable<[MODEL]>) {
    }

    @available(*, unavailable, message: "RxSwift 3.0 changed behavior of DataSources so we have to bind only once. Use bind(items: Observable<MODEL>)")
    open func bind(items: [MODEL]) {
    }
    
    open override func update() {
        var items: [MODEL] = []
        var emptyMessage = ""
        var loading = false
        
        switch componentState {
        case .items(let models):
            items = models
        case .empty(let message):
            emptyMessage = message
        case .loading:
            loading = true
        }
        
        emptyLabel.text = emptyMessage
        #if os(iOS)
        if let refreshControl = refreshControl {
            if loading {
                refreshControl.beginRefreshing()
            } else {
                refreshControl.endRefreshing()
            }
        } else {
            if loading {
                loadingIndicator.startAnimating()
            } else {
                loadingIndicator.stopAnimating()
            }
        }
        #else
        if loading {
            loadingIndicator.startAnimating()
        } else {
            loadingIndicator.stopAnimating()
        }
        #endif

        self.items.onNext(items)

        setNeedsLayout()
    }
    
    open func configure<T: Component>(cell: CollectionViewCellWrapper<T>, factory: @escaping () -> T, model: T.StateType,
                          mapAction: @escaping (T.ActionType) -> ACTION) -> Void {
        if configurationChangeTime != cell.configurationChangeTime {
            cell.configuration = configuration
            cell.configurationChangeTime = configurationChangeTime
        }
        let component = cell.cachedCellOrCreated(factory: factory)
        component.componentState = model
        component.action.map(mapAction)
            .subscribe(onNext: { [weak self] in
                self?.perform(action: $0)
            })
            .disposed(by: component.stateDisposeBag)
    }
    
    open func dequeueAndConfigure<T: Component>(identifier: CollectionViewCellIdentifier<T>, forRow row: Int, factory: @escaping () -> T,
                                    model: T.StateType, mapAction: @escaping (T.ActionType) -> ACTION) -> CollectionViewCellWrapper<T> {
        let cell = collectionView.dequeue(identifier: identifier, forRow: row)
        configure(cell: cell, factory: factory, model: model, mapAction: mapAction)
        return cell
    }
    
    open func configure<T: Component>(view: CollectionReusableViewWrapper<T>, factory: @escaping () -> T, model: T.StateType,
                          mapAction: @escaping (T.ActionType) -> ACTION) -> Void {
        if configurationChangeTime != view.configurationChangeTime {
            view.configuration = configuration
            view.configurationChangeTime = configurationChangeTime
        }
        let component = view.cachedViewOrCreated(factory: factory)
        component.componentState = model
        component.action.map(mapAction)
            .subscribe(onNext: { [weak self] in
                self?.perform(action: $0)
            })
            .disposed(by: component.stateDisposeBag)
    }
    
    open func dequeueAndConfigure<T: Component>(identifier: CollectionSupplementaryViewIdentifier<T>, forRow row: Int, factory: @escaping () -> T,
                                    model: T.StateType, mapAction: @escaping (T.ActionType) -> ACTION) -> CollectionReusableViewWrapper<T> {
        let view = collectionView.dequeue(identifier: identifier, forRow: row)
        configure(view: view, factory: factory, model: model, mapAction: mapAction)
        return view
    }
}

<commit_msg>Fix not working cellAction in CollectionViewBase.<commit_after>//
//  CollectionViewBase.swift
//  Reactant
//
//  Created by <NAME> on 21.11.16.
//  Copyright © 2016 Brightify. All rights reserved.
//

import RxSwift

open class CollectionViewBase<MODEL, ACTION>: ViewBase<CollectionViewState<MODEL>, ACTION>, ReactantCollectionView, UICollectionViewDelegate {
    
    open var edgesForExtendedLayout: UIRectEdge {
        return .all
    }
    
    open override var configuration: Configuration {
        didSet {
            configuration.get(valueFor: Properties.Style.CollectionView.collectionView)(self)
            
            configurationChangeTime = clock()
            setNeedsLayout()
        }
    }
    
    public let collectionView: UICollectionView
    #if os(iOS)
    public let refreshControl: UIRefreshControl?
    #endif
    public let emptyLabel = UILabel()
    public let loadingIndicator = UIActivityIndicatorView()
    
    private let items = PublishSubject<[MODEL]>()
    // Optimization that prevents configuration reloading each time cell is dequeued.
    private var configurationChangeTime: clock_t = clock()

    public init(layout: UICollectionViewLayout, reloadable: Bool = true) {
        self.collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
        #if os(iOS)
        self.refreshControl = reloadable ? UIRefreshControl() : nil
        #endif
        
        super.init()
    }
    
    open override func loadView() {
        children(
            collectionView,
            emptyLabel,
            loadingIndicator
        )
        #if os(iOS)
        if let refreshControl = refreshControl {
            collectionView.children(
                refreshControl
            )
        }
        #endif
        
        loadingIndicator.hidesWhenStopped = true
        
        collectionView.backgroundColor = .clear
        collectionView.rx.setDelegate(self).disposed(by: lifetimeDisposeBag)
    }
    
    open override func setupConstraints() {
        collectionView.snp.makeConstraints { make in
            make.edges.equalTo(self)
        }
        
        emptyLabel.snp.makeConstraints { make in
            make.center.equalTo(self)
        }
        
        loadingIndicator.snp.makeConstraints { make in
            make.center.equalTo(self)
        }
    }
    
    open override func afterInit() {
        collectionView.rx.itemSelected
            .subscribe(onNext: { [collectionView] in
                collectionView.deselectItem(at: $0, animated: true)
            })
            .disposed(by: lifetimeDisposeBag)

        bind(items: items)
    }

    open func bind(items: Observable<[MODEL]>) {
    }

    @available(*, unavailable, message: "RxSwift 3.0 changed behavior of DataSources so we have to bind only once. Use bind(items: Observable<MODEL>)")
    open func bind(items: [MODEL]) {
    }
    
    open override func update() {
        var items: [MODEL] = []
        var emptyMessage = ""
        var loading = false
        
        switch componentState {
        case .items(let models):
            items = models
        case .empty(let message):
            emptyMessage = message
        case .loading:
            loading = true
        }
        
        emptyLabel.text = emptyMessage
        #if os(iOS)
        if let refreshControl = refreshControl {
            if loading {
                refreshControl.beginRefreshing()
            } else {
                refreshControl.endRefreshing()
            }
        } else {
            if loading {
                loadingIndicator.startAnimating()
            } else {
                loadingIndicator.stopAnimating()
            }
        }
        #else
        if loading {
            loadingIndicator.startAnimating()
        } else {
            loadingIndicator.stopAnimating()
        }
        #endif

        self.items.onNext(items)

        setNeedsLayout()
    }
    
    open func configure<T: Component>(cell: CollectionViewCellWrapper<T>, factory: @escaping () -> T, model: T.StateType,
                          mapAction: @escaping (T.ActionType) -> ACTION) -> Void {
        if configurationChangeTime != cell.configurationChangeTime {
            cell.configuration = configuration
            cell.configurationChangeTime = configurationChangeTime
        }
        let component = cell.cachedCellOrCreated(factory: factory)
        component.componentState = model
        component.action.map(mapAction)
            .subscribe(onNext: { [weak self] in
                self?.perform(action: $0)
            })
            .disposed(by: stateDisposeBag)
    }
    
    open func dequeueAndConfigure<T: Component>(identifier: CollectionViewCellIdentifier<T>, forRow row: Int, factory: @escaping () -> T,
                                    model: T.StateType, mapAction: @escaping (T.ActionType) -> ACTION) -> CollectionViewCellWrapper<T> {
        let cell = collectionView.dequeue(identifier: identifier, forRow: row)
        configure(cell: cell, factory: factory, model: model, mapAction: mapAction)
        return cell
    }
    
    open func configure<T: Component>(view: CollectionReusableViewWrapper<T>, factory: @escaping () -> T, model: T.StateType,
                          mapAction: @escaping (T.ActionType) -> ACTION) -> Void {
        if configurationChangeTime != view.configurationChangeTime {
            view.configuration = configuration
            view.configurationChangeTime = configurationChangeTime
        }
        let component = view.cachedViewOrCreated(factory: factory)
        component.componentState = model
        component.action.map(mapAction)
            .subscribe(onNext: { [weak self] in
                self?.perform(action: $0)
            })
            .disposed(by: stateDisposeBag)
    }
    
    open func dequeueAndConfigure<T: Component>(identifier: CollectionSupplementaryViewIdentifier<T>, forRow row: Int, factory: @escaping () -> T,
                                    model: T.StateType, mapAction: @escaping (T.ActionType) -> ACTION) -> CollectionReusableViewWrapper<T> {
        let view = collectionView.dequeue(identifier: identifier, forRow: row)
        configure(view: view, factory: factory, model: model, mapAction: mapAction)
        return view
    }
}


{'fraction_non_alphanumeric': 0.08735941707587518, 'fraction_numerical': 0.002376049421827974, 'mean_word_length': 2.0433839479392626, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3622658', 'n_tokens_mistral': 3283, 'n_tokens_neox': 3225, 'n_words': 763}