<commit_before>//
//  LoadingBanner.swift
//  falkor
//
//  Created by <NAME> on 2/10/16.
//  Copyright © 2016 OC Tanner. All rights reserved.
//

import UIKit

@IBDesignable public class LoadingBanner: UIView {

    // MARK: - Public properties
    
    @IBInspectable public var showing: Bool = false {
        didSet {
            shouldShow = showing
            configureShowing()
        }
    }
    
    @IBInspectable public var backgroundTint: UIColor = UIColor.blueColor().colorWithAlphaComponent(0.2) {
        didSet {
            updateColors()
        }
    }
    
    @IBInspectable public var errorTint: UIColor = UIColor.redColor().colorWithAlphaComponent(0.2) {
        didSet {
            updateColors()
        }
    }
    
    @IBInspectable public var height: CGFloat = 24.0 {
        didSet {
            heightConstraint.constant = height
            layoutIfNeeded()
        }
    }
    
    
    // MARK: - Private properties
    
    private let visualEffectView: UIVisualEffectView = {
        let lightStyle = UIBlurEffectStyle.ExtraLight
        let lightBlurEffect = UIBlurEffect(style: lightStyle)
        return UIVisualEffectView(effect: lightBlurEffect)
    }()
    
    private let spinner = UIActivityIndicatorView(activityIndicatorStyle: .White)
    private var heightConstraint: NSLayoutConstraint!
    private let loadingStackView = UIStackView()
    private let errorStackView = UIStackView()
    private let errorLabel = UILabel()
    private var errorMessage: String?
    private var shouldShow = false
    
    
    // MARK: - Initializers
    
    override public init(frame: CGRect) {
        super.init(frame: frame)
        setupViews()
    }
    
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupViews()
    }
    
    
    // MARK: - Public functions
    
    public func showError(message: String?) {
        errorMessage = message
        errorLabel.text = message
        errorStackView.hidden = false
        loadingStackView.hidden = true
        shouldShow = true
        configureShowing()
    }
    
    

}


// MARK: - Internal functions

extension LoadingBanner {
    
    func dismissBanner() {
        shouldShow = false
        configureShowing({
            self.errorMessage = nil
            self.loadingStackView.hidden = false
            self.errorStackView.hidden = true
        })
    }

}


// MARK: - Private functions

private extension LoadingBanner {
    
    func configureShowing(completion: (() -> ())? = nil) {
        updateColors()
        UIView.animateWithDuration(0.0, animations: {
            // This is a hack to get the banner to start in the right place
            }) { finished in
                if self.shouldShow {
                    self.spinner.startAnimating()
                    self.heightConstraint.constant = self.height
                } else {
                    self.spinner.stopAnimating()
                    self.heightConstraint.constant = 0.0
                }
                UIView.animateWithDuration(0.3, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: [], animations: {
                    self.layoutIfNeeded()
                }) { complete in
                    completion?()
                }
        }
    }
    
    func updateColors() {
        if let _ = errorMessage {
            visualEffectView.contentView.backgroundColor = errorTint
        } else {
            visualEffectView.contentView.backgroundColor = backgroundTint
        }
    }
    
    
    // MARK: - Initial setup
    
    func setupViews() {
        backgroundColor = nil
        
        heightConstraint = heightAnchor.constraintEqualToConstant(0.0)
        heightConstraint.active = true
        
        addSubview(visualEffectView)
        setupFullSize(visualEffectView)
        
        let vibrancyEffect = UIVibrancyEffect(forBlurEffect: visualEffectView.effect as! UIBlurEffect)
        let vibrancyView = UIVisualEffectView(effect: vibrancyEffect)
        visualEffectView.contentView.addSubview(vibrancyView)
        setupFullSize(vibrancyView)
        
        vibrancyView.contentView.addSubview(loadingStackView)
        setupFullHeight(loadingStackView)
        loadingStackView.centerXAnchor.constraintEqualToAnchor(vibrancyView.centerXAnchor).active = true
        loadingStackView.spacing = 4.0
        
        loadingStackView.addArrangedSubview(spinner)
        spinner.hidesWhenStopped = false
        
        let label = UILabel()
        label.text = "Loading..."
        label.font = UIFont.systemFontOfSize(UIFont.smallSystemFontSize())
        loadingStackView.addArrangedSubview(label)
        
        vibrancyView.contentView.addSubview(errorStackView)
        setupFullSize(errorStackView)
        
        errorStackView.addArrangedSubview(errorLabel)<commit_msg>Simplify exposed and internal API<commit_after>//
//  LoadingBanner.swift
//  falkor
//
//  Created by <NAME> on 2/10/16.
//  Copyright © 2016 OC Tanner. All rights reserved.
//

import UIKit

@IBDesignable public class LoadingBanner: UIView {

    // MARK: - Public properties
    
    @IBInspectable public var backgroundTint: UIColor = UIColor.blueColor().colorWithAlphaComponent(0.2) {
        didSet {
            updateColors()
        }
    }
    
    @IBInspectable public var errorTint: UIColor = UIColor.redColor().colorWithAlphaComponent(0.2) {
        didSet {
            updateColors()
        }
    }
    
    @IBInspectable public var height: CGFloat = 24.0 {
        didSet {
            heightConstraint.constant = height
            layoutIfNeeded()
        }
    }
    
    @IBInspectable public var loadingText: String = "Loading…" {
        didSet {
            loadingLabel.text = loadingText
        }
    }
    
    
    // MARK: - Private properties
    
    private let visualEffectView: UIVisualEffectView = {
        let lightStyle = UIBlurEffectStyle.ExtraLight
        let lightBlurEffect = UIBlurEffect(style: lightStyle)
        return UIVisualEffectView(effect: lightBlurEffect)
    }()
    
    private let spinner = UIActivityIndicatorView(activityIndicatorStyle: .White)
    private var heightConstraint: NSLayoutConstraint!
    private let loadingStackView = UIStackView()
    private let loadingLabel = UILabel()
    private let errorStackView = UIStackView()
    private let errorLabel = UILabel()
    private var errorMessage: String?
    private var showing = false
    
    
    // MARK: - Initializers
    
    override public init(frame: CGRect) {
        super.init(frame: frame)
        setupViews()
    }
    
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupViews()
    }
    
    
    // MARK: - Public functions
    
    public func showLoading() {
        errorMessage = nil
        loadingStackView.hidden = false
        errorStackView.hidden = true
        showing = true
        showBanner()
    }
    
    public func dismiss() {
        dismissBanner()
    }
    
    public func showError(message: String?) {
        errorMessage = message
        errorLabel.text = message
        errorStackView.hidden = false
        loadingStackView.hidden = true
        showing = true
        showBanner()
    }

}


// MARK: - Internal functions

extension LoadingBanner {
    
    func dismissBanner() {
        showing = false
        showBanner({
            self.errorMessage = nil
            self.loadingStackView.hidden = false
            self.errorStackView.hidden = true
        })
    }

}


// MARK: - Private functions

private extension LoadingBanner {
    
    func showBanner(completion: (() -> ())? = nil) {
        updateColors()
        UIView.animateWithDuration(0.0, animations: {
            // This is a hack to get the banner to start in the right place
            }) { finished in
                if self.showing {
                    self.spinner.startAnimating()
                    self.heightConstraint.constant = self.height
                } else {
                    self.spinner.stopAnimating()
                    self.heightConstraint.constant = 0.0
                }
                UIView.animateWithDuration(0.3, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: [], animations: {
                    self.layoutIfNeeded()
                }) { complete in
                    completion?()
                }
        }
    }
    
    func updateColors() {
        if let _ = errorMessage {
            visualEffectView.contentView.backgroundColor = errorTint
        } else {
            visualEffectView.contentView.backgroundColor = backgroundTint
        }
    }
    
    
    // MARK: - Initial setup
    
    func setupViews() {
        backgroundColor = nil
        
        heightConstraint = heightAnchor.constraintEqualToConstant(0.0)
        heightConstraint.active = true
        
        addSubview(visualEffectView)
        setupFullSize(visualEffectView)
        
        let vibrancyEffect = UIVibrancyEffect(forBlurEffect: visualEffectView.effect as! UIBlurEffect)
        let vibrancyView = UIVisualEffectView(effect: vibrancyEffect)
        visualEffectView.contentView.addSubview(vibrancyView)
        setupFullSize(vibrancyView)
        
        vibrancyView.contentView.addSubview(loadingStackView)
        setupFullHeight(loadingStackView)
        loadingStackView.centerXAnchor.constraintEqualToAnchor(vibrancyView.centerXAnchor).active = true
        loadingStackView.spacing = 4.0
        
        loadingStackView.addArrangedSubview(spinner)
        spinner.hidesWhenStopped = false
        
        loadingLabel.text = loadingText
        loadingLabel.font = UIFont.systemFontOfSize(UIFont.smallSystemFontSize())
        loadingStackView.addArrangedSubview(loadingLabel)
        
        vibrancyView.contentView.addSubview(errorStackView)
        setupFullSize(errorStackView)
        
        errorStackView.addArrangedSubview(errorLabel)
{'fraction_non_alphanumeric': 0.0775758785942492, 'fraction_numerical': 0.006389776357827476, 'mean_word_length': 2.0090117152297986, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2865710', 'n_tokens_mistral': 2778, 'n_tokens_neox': 2720, 'n_words': 547}