diff --git a/Signal/src/ViewControllers/GifPicker/GifPickerViewController.swift b/Signal/src/ViewControllers/GifPicker/GifPickerViewController.swift index 959ff7ca0..5c039402a 100644 --- a/Signal/src/ViewControllers/GifPicker/GifPickerViewController.swift +++ b/Signal/src/ViewControllers/GifPicker/GifPickerViewController.swift @@ -15,6 +15,18 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect // MARK: Properties + enum ViewMode { + case idle, searching, results, noResults, error + } + + private var viewMode = ViewMode.idle { + didSet { + Logger.info("\(TAG) viewMode: \(viewMode)") + + updateContents() + } + } + public weak var delegate: GifPickerViewControllerDelegate? var thread: TSThread? @@ -23,6 +35,9 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect let searchBar: UISearchBar let layout: GifPickerLayout let collectionView: UICollectionView + var noResultsView: UILabel? + var searchErrorView: UILabel? + var activityIndicator: UIActivityIndicatorView? var imageInfos = [GiphyImageInfo]() @@ -159,12 +174,91 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect logoImageView.autoPinHeightToSuperview(withMargin:3) logoImageView.autoHCenterInSuperview() + let noResultsView = createErrorLabel(text:NSLocalizedString("GIF_VIEW_SEARCH_NO_RESULTS", + comment:"Indicates that the user's search had no results.")) + self.noResultsView = noResultsView + self.view.addSubview(noResultsView) + noResultsView.autoPinWidthToSuperview(withMargin:20) + noResultsView.autoVCenterInSuperview() + + let searchErrorView = createErrorLabel(text:NSLocalizedString("GIF_VIEW_SEARCH_ERROR", + comment:"Indicates that an error occured while searching.")) + self.searchErrorView = searchErrorView + self.view.addSubview(searchErrorView) + searchErrorView.autoPinWidthToSuperview(withMargin:20) + searchErrorView.autoVCenterInSuperview() + + searchErrorView.isUserInteractionEnabled = true + searchErrorView.addGestureRecognizer(UITapGestureRecognizer(target:self, action:#selector(retryTapped))) + + let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle:.gray) + self.activityIndicator = activityIndicator + self.view.addSubview(activityIndicator) + activityIndicator.autoCenterInSuperview() + self.updateContents() } + private func createErrorLabel(text: String) -> UILabel { + let label = UILabel() + label.text = text + label.textColor = UIColor.black + label.font = UIFont.ows_mediumFont(withSize:20) + label.textAlignment = .center + label.numberOfLines = 0 + label.lineBreakMode = .byWordWrapping + return label + } + private func updateContents() { - self.collectionView.collectionViewLayout.invalidateLayout() - self.collectionView.reloadData() + guard let noResultsView = self.noResultsView else { + owsFail("Missing noResultsView") + return + } + guard let searchErrorView = self.searchErrorView else { + owsFail("Missing searchErrorView") + return + } + guard let activityIndicator = self.activityIndicator else { + owsFail("Missing activityIndicator") + return + } + + switch viewMode { + case .idle: + self.collectionView.isHidden = true + noResultsView.isHidden = true + searchErrorView.isHidden = true + activityIndicator.isHidden = true + activityIndicator.stopAnimating() + case .searching: + self.collectionView.isHidden = true + noResultsView.isHidden = true + searchErrorView.isHidden = true + activityIndicator.isHidden = false + activityIndicator.startAnimating() + case .results: + self.collectionView.isHidden = false + noResultsView.isHidden = true + searchErrorView.isHidden = true + activityIndicator.isHidden = true + activityIndicator.stopAnimating() + + self.collectionView.collectionViewLayout.invalidateLayout() + self.collectionView.reloadData() + case .noResults: + self.collectionView.isHidden = true + noResultsView.isHidden = false + searchErrorView.isHidden = true + activityIndicator.isHidden = true + activityIndicator.stopAnimating() + case .error: + self.collectionView.isHidden = true + noResultsView.isHidden = true + searchErrorView.isHidden = false + activityIndicator.isHidden = true + activityIndicator.stopAnimating() + } } // MARK: - UICollectionViewDataSource @@ -250,6 +344,10 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect } public func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { + tryToSearch() + } + + public func tryToSearch() { guard let text = searchBar.text else { OWSAlerts.showAlert(withTitle: NSLocalizedString("ALERT_ERROR_TITLE", comment: ""), @@ -263,19 +361,24 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect private func search(query: String) { self.searchBar.resignFirstResponder() imageInfos = [] - updateContents() + viewMode = .searching self.collectionView.contentOffset = CGPoint.zero GiphyAPI.sharedInstance.search(query: query, success: { [weak self] imageInfos in guard let strongSelf = self else { return } Logger.info("\(strongSelf.TAG) search complete") strongSelf.imageInfos = imageInfos - strongSelf.updateContents() + if imageInfos.count > 0 { + strongSelf.viewMode = .results + } else { + strongSelf.viewMode = .noResults + } }, failure: { [weak self] _ in guard let strongSelf = self else { return } Logger.info("\(strongSelf.TAG) search failed.") // TODO: Present this error to the user. + strongSelf.viewMode = .error }) } @@ -284,4 +387,16 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect func imageInfosForLayout() -> [GiphyImageInfo] { return imageInfos } + + // MARK: - Event Handlers + + func retryTapped(sender: UIGestureRecognizer) { + guard sender.state == .recognized else { + return + } + guard viewMode == .error else { + return + } + tryToSearch() + } } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 0a2dc6f9d..f40ce005b 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -610,6 +610,12 @@ /* Title for the 'gif picker' dialog. */ "GIF_PICKER_VIEW_TITLE" = "GIF Search"; +/* Indicates that an error occured while searching. */ +"GIF_VIEW_SEARCH_ERROR" = "Error. Tap to Retry."; + +/* Indicates that the user's search had no results. */ +"GIF_VIEW_SEARCH_NO_RESULTS" = "No Results."; + /* Placeholder text for the search field in gif view */ "GIF_VIEW_SEARCH_PLACEHOLDER_TEXT" = "Enter your search";