Skip to content

Commit

Permalink
Prepare suggestions to handle xposts
Browse files Browse the repository at this point in the history
- Update `SuggestionsTableView.m/.h` to use a more succinct initializer
- Add new logic to `SuggestionsTableView.swift` to handle multiple mention types (i.e. mentions, and in the future, xposts)
- Rename variable names in `SuggestionsTableViewCell.m/.h` to be @-mention agnostic
- Rename `GutenbergMentionsViewController` to `GutenbergSuggestionsViewController` to reflect its role supporting not just mentions, but also xposts in the near future
- Rename variable names in `GutenbergViewController` to be @-mention agnostic
  • Loading branch information
guarani committed Oct 21, 2020
1 parent 207f06c commit 6966f0d
Show file tree
Hide file tree
Showing 15 changed files with 258 additions and 212 deletions.
33 changes: 0 additions & 33 deletions WordPress/Classes/Models/UserSuggestion+ImageHelpers.swift

This file was deleted.

1 change: 1 addition & 0 deletions WordPress/Classes/System/WordPress-Bridging-Header.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
#import "SourcePostAttribution.h"
#import "StatsViewController.h"
#import "SuggestionsTableView.h"
#import "SuggestionsTableViewCell.h"
#import "SVProgressHUD+Dismiss.h"

#import "Theme.h"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,7 @@ - (void)attachSuggestionsTableViewIfNeeded
return;
}

self.suggestionsTableView = [SuggestionsTableView new];
self.suggestionsTableView.siteID = self.comment.blog.dotComID;
self.suggestionsTableView.suggestionsDelegate = self;
self.suggestionsTableView = [[SuggestionsTableView alloc] initWithSiteID:self.comment.blog.dotComID suggestionType:SuggestionTypeMention delegate:self];
[self.suggestionsTableView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:self.suggestionsTableView];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,8 @@ public class FullScreenCommentReplyViewController: EditCommentViewController, Su
return
}

let tableView = SuggestionsTableView()
tableView.siteID = siteID
tableView.suggestionsDelegate = self
guard let siteID = siteID else { return }
let tableView = SuggestionsTableView(siteID: siteID, suggestionType: .mention, delegate: self)
tableView.useTransparentHeader = true
tableView.translatesAutoresizingMaskIntoConstraints = false

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import Foundation
import UIKit

public class GutenbergMentionsViewController: UIViewController {
public class GutenbergSuggestionsViewController: UIViewController {

static let mentionTriggerText = String("@")
static let minimumHeaderHeight = CGFloat(50)

public lazy var backgroundView: UIView = {
Expand All @@ -23,8 +22,7 @@ public class GutenbergMentionsViewController: UIViewController {

public lazy var searchView: UITextField = {
let textField = UITextField(frame: CGRect.zero)
textField.placeholder = NSLocalizedString("Search users...", comment: "Placeholder message when showing mentions search field")
textField.text = Self.mentionTriggerText
textField.text = suggestionType.trigger
textField.clearButtonMode = .whileEditing
textField.translatesAutoresizingMaskIntoConstraints = false
textField.delegate = self
Expand All @@ -33,23 +31,22 @@ public class GutenbergMentionsViewController: UIViewController {
}()

public lazy var suggestionsView: SuggestionsTableView = {
let suggestionsView = SuggestionsTableView()
let suggestionsView = SuggestionsTableView(siteID: siteID, suggestionType: suggestionType, delegate: self)
suggestionsView.animateWithKeyboard = false
suggestionsView.enabled = true
suggestionsView.showLoading = true
suggestionsView.showSuggestions(forWord: Self.mentionTriggerText)
suggestionsView.suggestionsDelegate = self
suggestionsView.translatesAutoresizingMaskIntoConstraints = false
suggestionsView.siteID = siteID
suggestionsView.useTransparentHeader = false
return suggestionsView
}()

private let siteID: NSNumber
private let suggestionType: SuggestionType
public var onCompletion: ((Result<String, NSError>) -> Void)?

public init(siteID: NSNumber) {
public init(siteID: NSNumber, suggestionType: SuggestionType) {
self.siteID = siteID
self.suggestionType = suggestionType
super.init(nibName: nil, bundle: nil)
}

Expand Down Expand Up @@ -101,11 +98,11 @@ public class GutenbergMentionsViewController: UIViewController {
}

override public func viewDidAppear(_ animated: Bool) {
suggestionsView.showSuggestions(forWord: Self.mentionTriggerText)
suggestionsView.showSuggestions(forWord: suggestionType.trigger)
}
}

extension GutenbergMentionsViewController: UITextFieldDelegate {
extension GutenbergSuggestionsViewController: UITextFieldDelegate {

public func textFieldShouldClear(_ textField: UITextField) -> Bool {
onCompletion?(.failure(buildErrorForCancelation()))
Expand All @@ -117,7 +114,7 @@ extension GutenbergMentionsViewController: UITextFieldDelegate {
return true
}
let searchWord = nsString.replacingCharacters(in: range, with: string)
if searchWord.hasPrefix(Self.mentionTriggerText) {
if searchWord.hasPrefix(suggestionType.trigger) {
suggestionsView.showSuggestions(forWord: searchWord)
} else {
// We are dispatching this async to allow this delegate to finish and process the keypress before executing the cancelation.
Expand All @@ -136,7 +133,7 @@ extension GutenbergMentionsViewController: UITextFieldDelegate {
}
}

extension GutenbergMentionsViewController: SuggestionsTableViewDelegate {
extension GutenbergSuggestionsViewController: SuggestionsTableViewDelegate {

public func suggestionsTableView(_ suggestionsTableView: SuggestionsTableView, didSelectSuggestion suggestion: String?, forSearchText text: String) {
if let suggestion = suggestion {
Expand All @@ -161,13 +158,13 @@ extension GutenbergMentionsViewController: SuggestionsTableViewDelegate {
}
}

extension GutenbergMentionsViewController {
extension GutenbergSuggestionsViewController {

enum MentionError: CustomNSError {
enum SuggestionError: CustomNSError {
case canceled
case notAvailable

static var errorDomain: String = "MentionErrorDomain"
static var errorDomain: String = "SuggestionErrorDomain"

var errorCode: Int {
switch self {
Expand All @@ -184,6 +181,6 @@ extension GutenbergMentionsViewController {
}

private func buildErrorForCancelation() -> NSError {
return MentionError.canceled as NSError
return SuggestionError.canceled as NSError
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ class GutenbergViewController: UIViewController, PostEditor {
private var keyboardShowObserver: Any?
private var keyboardHideObserver: Any?
private var keyboardFrame = CGRect.zero
private var mentionsBottomConstraint: NSLayoutConstraint?
private var suggestionViewBottomConstraint: NSLayoutConstraint?
private var previousFirstResponder: UIView?

private func setupKeyboardObservers() {
Expand Down Expand Up @@ -835,23 +835,23 @@ extension GutenbergViewController: GutenbergBridgeDelegate {
func updateConstraintsToAvoidKeyboard(frame: CGRect) {
keyboardFrame = frame
let minimumKeyboardHeight = CGFloat(50)
guard let mentionsBottomConstraint = mentionsBottomConstraint else {
guard let suggestionViewBottomConstraint = suggestionViewBottomConstraint else {
return
}

// There are cases where the keyboard is not visible, but the system instead of returning zero, returns a low number, for example: 0, 3, 69.
// So in those scenarios, we just need to take in account the safe area and ignore the keyboard all together.
if keyboardFrame.height < minimumKeyboardHeight {
mentionsBottomConstraint.constant = -self.view.safeAreaInsets.bottom
suggestionViewBottomConstraint.constant = -self.view.safeAreaInsets.bottom
}
else {
mentionsBottomConstraint.constant = -self.keyboardFrame.height
suggestionViewBottomConstraint.constant = -self.keyboardFrame.height
}
}

func gutenbergDidRequestMention(callback: @escaping (Swift.Result<String, NSError>) -> Void) {
DispatchQueue.main.async(execute: { [weak self] in
self?.mentionShow(callback: callback)
self?.showSuggestions(type: .mention, callback: callback)
})
}

Expand All @@ -871,40 +871,43 @@ extension GutenbergViewController: GutenbergBridgeDelegate {
}
}

// MARK: - Mention implementation
// MARK: - Suggestions implementation

extension GutenbergViewController {

private func mentionShow(callback: @escaping (Swift.Result<String, NSError>) -> Void) {
guard let siteID = post.blog.dotComID,
let blog = SuggestionService.shared.persistedBlog(for: siteID),
SuggestionService.shared.shouldShowSuggestions(for: blog) else {
callback(.failure(GutenbergMentionsViewController.MentionError.notAvailable as NSError))
private func showSuggestions(type: SuggestionType, callback: @escaping (Swift.Result<String, NSError>) -> Void) {
guard let siteID = post.blog.dotComID, let blog = SuggestionService.shared.persistedBlog(for: siteID) else {
callback(.failure(GutenbergSuggestionsViewController.SuggestionError.notAvailable as NSError))
return
}

switch type {
case .mention:
guard SuggestionService.shared.shouldShowSuggestions(for: blog) else { return }
}

previousFirstResponder = view.findFirstResponder()
let mentionsController = GutenbergMentionsViewController(siteID: siteID)
mentionsController.onCompletion = { (result) in
let suggestionsController = GutenbergSuggestionsViewController(siteID: siteID, suggestionType: type)
suggestionsController.onCompletion = { (result) in
callback(result)
mentionsController.view.removeFromSuperview()
mentionsController.removeFromParent()
suggestionsController.view.removeFromSuperview()
suggestionsController.removeFromParent()
if let previousFirstResponder = self.previousFirstResponder {
previousFirstResponder.becomeFirstResponder()
}
}
addChild(mentionsController)
view.addSubview(mentionsController.view)
let mentionsBottomConstraint = mentionsController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0)
addChild(suggestionsController)
view.addSubview(suggestionsController.view)
let suggestionsBottomConstraint = suggestionsController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0)
NSLayoutConstraint.activate([
mentionsController.view.leadingAnchor.constraint(equalTo: view.safeLeadingAnchor, constant: 0),
mentionsController.view.trailingAnchor.constraint(equalTo: view.safeTrailingAnchor, constant: 0),
mentionsBottomConstraint,
mentionsController.view.topAnchor.constraint(equalTo: view.safeTopAnchor)
suggestionsController.view.leadingAnchor.constraint(equalTo: view.safeLeadingAnchor, constant: 0),
suggestionsController.view.trailingAnchor.constraint(equalTo: view.safeTrailingAnchor, constant: 0),
suggestionsBottomConstraint,
suggestionsController.view.topAnchor.constraint(equalTo: view.safeTopAnchor)
])
self.mentionsBottomConstraint = mentionsBottomConstraint
self.suggestionViewBottomConstraint = suggestionsBottomConstraint
updateConstraintsToAvoidKeyboard(frame: keyboardFrame)
mentionsController.didMove(toParent: self)
suggestionsController.didMove(toParent: self)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -434,9 +434,8 @@ extension NotificationDetailsViewController {
}

func setupSuggestionsView() {
suggestionsTableView = SuggestionsTableView()
suggestionsTableView.siteID = note.metaSiteID
suggestionsTableView.suggestionsDelegate = self
guard let siteID = note.metaSiteID else { return }
suggestionsTableView = SuggestionsTableView(siteID: siteID, suggestionType: .mention, delegate: self)
suggestionsTableView.translatesAutoresizingMaskIntoConstraints = false
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -385,9 +385,7 @@ - (void)configureSuggestionsTableView
NSNumber *siteID = self.siteID;
NSParameterAssert(siteID);

self.suggestionsTableView = [SuggestionsTableView new];
self.suggestionsTableView.siteID = siteID;
self.suggestionsTableView.suggestionsDelegate = self;
self.suggestionsTableView = [[SuggestionsTableView alloc] initWithSiteID:siteID suggestionType:SuggestionTypeMention delegate:self];
[self.suggestionsTableView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:self.suggestionsTableView];
}
Expand Down
15 changes: 12 additions & 3 deletions WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.h
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
#import <UIKit/UIKit.h>

typedef NS_CLOSED_ENUM(NSUInteger, SuggestionType) {
SuggestionTypeMention
};

@protocol SuggestionsTableViewDelegate;

@interface SuggestionsTableView : UIView <UITableViewDataSource, UITableViewDelegate>
@interface SuggestionsTableView : UIView <UITableViewDataSource>

@property (nonatomic, nullable, weak) id <SuggestionsTableViewDelegate> suggestionsDelegate;
@property (nonatomic, nullable, strong) NSNumber *siteID;
@property (nonatomic, assign) SuggestionType suggestionType;
@property (nonatomic, nonnull, strong) NSMutableArray *searchResults;
@property (nonatomic, nullable, strong) NSArray *suggestions;
@property (nonatomic, nonnull, strong) NSString *searchText;
@property (nonatomic) BOOL useTransparentHeader;
@property (nonatomic) BOOL animateWithKeyboard;
@property (nonatomic) BOOL showLoading;

- (nonnull instancetype)init;

- (nonnull instancetype)initWithSiteID:(NSNumber *_Nullable)siteID
suggestionType:(SuggestionType)suggestionType
delegate:(id <SuggestionsTableViewDelegate>_Nonnull)suggestionsDelegate;

/**
Enables or disables the SuggestionsTableView component.
Expand Down
Loading

0 comments on commit 6966f0d

Please sign in to comment.