diff --git a/Classes/Issues/Comments/IssueCommentSectionController.swift b/Classes/Issues/Comments/IssueCommentSectionController.swift index 888058123..adf4a8832 100644 --- a/Classes/Issues/Comments/IssueCommentSectionController.swift +++ b/Classes/Issues/Comments/IssueCommentSectionController.swift @@ -11,6 +11,7 @@ import IGListKit import TUSafariActivity import Squawk import GitHubAPI +import ContextMenu protocol IssueCommentSectionControllerDelegate: class { func didSelectReply( @@ -27,7 +28,8 @@ final class IssueCommentSectionController: IssueCommentReactionCellDelegate, EditCommentViewControllerDelegate, MarkdownStyledTextViewDelegate, - IssueCommentDoubleTapDelegate { + IssueCommentDoubleTapDelegate, + ContextMenuDelegate { private weak var issueCommentDelegate: IssueCommentSectionControllerDelegate? @@ -37,7 +39,6 @@ final class IssueCommentSectionController: private let model: IssueDetailsModel private var hasBeenDeleted = false private let autocomplete: IssueCommentAutocomplete - private var menuVisible = false private lazy var webviewCache: WebviewCellHeightCache = { return WebviewCellHeightCache(sectionController: self) @@ -183,7 +184,7 @@ final class IssueCommentSectionController: @discardableResult private func uncollapse() -> Bool { - guard collapsed, !menuVisible else { return false } + guard collapsed else { return false } collapsed = false clearCollapseCells() collectionContext?.invalidateLayout(for: self, completion: nil) @@ -468,14 +469,6 @@ final class IssueCommentSectionController: // MARK: IssueCommentReactionCellDelegate - func willShowMenu(cell: IssueCommentReactionCell) { - menuVisible = true - } - - func didHideMenu(cell: IssueCommentReactionCell) { - menuVisible = false - } - func didAdd(cell: IssueCommentReactionCell, reaction: ReactionContent) { // don't add a reaction if already reacted guard let reactions = reactionMutation ?? self.object?.reactions, @@ -518,6 +511,26 @@ final class IssueCommentSectionController: viewController?.present(alert, animated: trueUnlessReduceMotionEnabled) } + func didTapAddReaction(cell: IssueCommentReactionCell, sender: UIView) { + guard let viewController = self.viewController else { return } + ContextMenu.shared.show( + sourceViewController: viewController, + viewController: ReactionsMenuViewController(), + options: ContextMenu.Options( + durations: ContextMenu.AnimationDurations(present: 0.2), + containerStyle: ContextMenu.ContainerStyle( + xPadding: -4, + yPadding: 8, + backgroundColor: Styles.Colors.menuBackgroundColor.color + ), + menuStyle: .minimal, + hapticsStyle: .medium + ), + sourceView: sender, + delegate: self + ) + } + // MARK: MarkdownStyledTextViewDelegate func didTap(cell: MarkdownStyledTextView, attribute: DetectedMarkdownAttribute) { @@ -535,4 +548,30 @@ final class IssueCommentSectionController: viewController.dismiss(animated: trueUnlessReduceMotionEnabled) } + // MARK: ContextMenuDelegate + + func contextMenuWillDismiss(viewController: UIViewController, animated: Bool) {} + + func contextMenuDidDismiss(viewController: UIViewController, animated: Bool) { + guard let reactionViewController = viewController as? ReactionsMenuViewController, + let reaction = reactionViewController.selectedReaction, + let reactions = reactionMutation ?? self.object?.reactions + else { return } + + var index = -1 + for (i, model) in viewModels.reversed().enumerated() { + if model is IssueCommentReactionViewModel { + index = viewModels.count - 1 - i + break + } + } + + guard index >= 0 else { return } + react( + cell: collectionContext?.cellForItem(at: index, sectionController: self) as? IssueCommentReactionCell, + content: reaction, + isAdd: !reactions.viewerDidReact(reaction: reaction) + ) + } + } diff --git a/Classes/Issues/Comments/Reactions/IssueCommentReactionCell.swift b/Classes/Issues/Comments/Reactions/IssueCommentReactionCell.swift index a3d795a91..69c40fd9e 100644 --- a/Classes/Issues/Comments/Reactions/IssueCommentReactionCell.swift +++ b/Classes/Issues/Comments/Reactions/IssueCommentReactionCell.swift @@ -11,11 +11,10 @@ import SnapKit import IGListKit protocol IssueCommentReactionCellDelegate: class { - func willShowMenu(cell: IssueCommentReactionCell) - func didHideMenu(cell: IssueCommentReactionCell) func didAdd(cell: IssueCommentReactionCell, reaction: ReactionContent) func didRemove(cell: IssueCommentReactionCell, reaction: ReactionContent) func didTapMore(cell: IssueCommentReactionCell, sender: UIView) + func didTapAddReaction(cell: IssueCommentReactionCell, sender: UIView) } final class IssueCommentReactionCell: IssueCommentBaseCell, @@ -49,7 +48,7 @@ UICollectionViewDelegateFlowLayout { addButton.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10) addButton.setTitleColor(Styles.Colors.Gray.light.color, for: .normal) addButton.setImage(UIImage(named: "smiley-small")?.withRenderingMode(.alwaysTemplate), for: .normal) - addButton.addTarget(self, action: #selector(IssueCommentReactionCell.onAddButton), for: .touchUpInside) + addButton.addTarget(self, action: #selector(IssueCommentReactionCell.onAddButton(sender:)), for: .touchUpInside) addButton.accessibilityLabel = NSLocalizedString("Add reaction", comment: "") addButton.setContentCompressionResistancePriority(.required, for: .horizontal) contentView.addSubview(addButton) @@ -84,20 +83,6 @@ UICollectionViewDelegateFlowLayout { make.top.bottom.right.equalToSuperview() make.right.equalTo(moreButton.snp.left).offset(-Styles.Sizes.columnSpacing) } - - let nc = NotificationCenter.default - nc.addObserver( - self, - selector: #selector(onMenuControllerWillShow(notification:)), - name: .UIMenuControllerWillShowMenu, - object: nil - ) - nc.addObserver( - self, - selector: #selector(onMenuControllerDidHide(notification:)), - name: .UIMenuControllerDidHideMenu, - object: nil - ) } required init?(coder aDecoder: NSCoder) { @@ -123,22 +108,8 @@ UICollectionViewDelegateFlowLayout { return collectionView.cellForItem(at: path) as? IssueReactionCell } - @objc private func onAddButton() { - addButton.becomeFirstResponder() - - let actions = [ - (ReactionContent.thumbsUp.emoji, #selector(IssueCommentReactionCell.onThumbsUp)), - (ReactionContent.thumbsDown.emoji, #selector(IssueCommentReactionCell.onThumbsDown)), - (ReactionContent.laugh.emoji, #selector(IssueCommentReactionCell.onLaugh)), - (ReactionContent.hooray.emoji, #selector(IssueCommentReactionCell.onHooray)), - (ReactionContent.confused.emoji, #selector(IssueCommentReactionCell.onConfused)), - (ReactionContent.heart.emoji, #selector(IssueCommentReactionCell.onHeart)) - ] - - let menu = UIMenuController.shared - menu.menuItems = actions.map { UIMenuItem(title: $0.0, action: $0.1) } - menu.setTargetRect(addButton.imageView?.frame ?? .zero, in: addButton) - menu.setMenuVisible(true, animated: trueUnlessReduceMotionEnabled) + @objc private func onAddButton(sender: UIView) { + delegate?.didTapAddReaction(cell: self, sender: sender) } override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { @@ -208,16 +179,6 @@ UICollectionViewDelegateFlowLayout { delegate?.didTapMore(cell: self, sender: sender) } - // MARK: Notifications - - @objc func onMenuControllerWillShow(notification: Notification) { - delegate?.willShowMenu(cell: self) - } - - @objc func onMenuControllerDidHide(notification: Notification) { - delegate?.didHideMenu(cell: self) - } - // MARK: ListBindable func bindViewModel(_ viewModel: Any) { diff --git a/Classes/Issues/Comments/Reactions/ReactionsMenuViewController.swift b/Classes/Issues/Comments/Reactions/ReactionsMenuViewController.swift new file mode 100644 index 000000000..ece3464d6 --- /dev/null +++ b/Classes/Issues/Comments/Reactions/ReactionsMenuViewController.swift @@ -0,0 +1,107 @@ +// +// ReactionsMenuViewController.swift +// Freetime +// +// Created by Ryan Nystrom on 8/12/18. +// Copyright © 2018 Ryan Nystrom. All rights reserved. +// + +import UIKit + +final class EmojiCell: UICollectionViewCell { + let label = UILabel() + + override init(frame: CGRect) { + super.init(frame: frame) + label.textAlignment = .center + label.backgroundColor = .clear + label.font = UIFont.systemFont(ofSize: Styles.Text.body.size + 4) + contentView.addSubview(label) + + selectedBackgroundView = UIView() + selectedBackgroundView?.backgroundColor = Styles.Colors.Gray.medium.color + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + label.frame = contentView.bounds + } + +} + +final class ReactionsMenuViewController: UICollectionViewController, + UICollectionViewDelegateFlowLayout { + + private let reuseIdentifier = "cell" + private let size: CGFloat = 50 + private let reactions: [ReactionContent] = [ + .thumbsUp, + .hooray, + .thumbsDown, + .heart, + .laugh, + .confused + ] + + var selectedReaction: ReactionContent? { + guard let item = collectionView?.indexPathsForSelectedItems?.first?.item else { return nil } + return reactions[item] + } + + init() { + let layout = UICollectionViewFlowLayout() + layout.minimumLineSpacing = 0 + layout.minimumInteritemSpacing = 0 + super.init(collectionViewLayout: layout) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + collectionView?.backgroundColor = Styles.Colors.menuBackgroundColor.color + collectionView?.register(EmojiCell.self, forCellWithReuseIdentifier: reuseIdentifier) + collectionView?.reloadData() + collectionView?.layoutIfNeeded() + preferredContentSize = CGSize( + width: size * CGFloat(reactions.count), + height: size + ) + } + + // MARK: UICollectionViewDataSource + + override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return reactions.count + } + + override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) + if let cell = cell as? EmojiCell { + cell.label.text = reactions[indexPath.item].emoji + } + return cell + } + + // MARK: UICollectionViewDelegate + + override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + dismiss(animated: true) + } + + // MARK: UICollectionViewDelegateFlowLayout + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return CGSize( + width: size, + height: size + ) + } + +} diff --git a/Freetime.xcodeproj/project.pbxproj b/Freetime.xcodeproj/project.pbxproj index 1d6a219d1..bb53849e3 100644 --- a/Freetime.xcodeproj/project.pbxproj +++ b/Freetime.xcodeproj/project.pbxproj @@ -8,9 +8,6 @@ /* Begin PBXBuildFile section */ 0F9440FD32236514CD7215E9 /* Pods_Freetime.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECF622FFD773FDA73297C0D0 /* Pods_Freetime.framework */; }; - 15245487205DC016005810A6 /* IssueTargetBranchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15245484205DC016005810A6 /* IssueTargetBranchModel.swift */; }; - 15245488205DC016005810A6 /* IssueBranchesSectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15245485205DC016005810A6 /* IssueBranchesSectionController.swift */; }; - 15245489205DC016005810A6 /* IssueTargetBranchCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15245486205DC016005810A6 /* IssueTargetBranchCell.swift */; }; 15F28F992108DBA6006421B6 /* SplashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F28F982108DBA6006421B6 /* SplashView.swift */; }; 290056F3210028B20046EAE5 /* UIViewController+MenuDone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290056F2210028B20046EAE5 /* UIViewController+MenuDone.swift */; }; 2905AFAB1F7357B40015AE32 /* RepositoryOverviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2905AFAA1F7357B40015AE32 /* RepositoryOverviewViewController.swift */; }; @@ -327,6 +324,10 @@ 29BBD82920CAC7D5004D62FE /* NotificationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29BBD82820CAC7D5004D62FE /* NotificationViewModel.swift */; }; 29BBD82B20CACB2F004D62FE /* NotificationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29BBD82A20CACB2F004D62FE /* NotificationCell.swift */; }; 29BBD82D20CACDFF004D62FE /* NotificationSectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29BBD82C20CACDFF004D62FE /* NotificationSectionController.swift */; }; + 29BCA7FE212137E100753A3C /* IssueTargetBranchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29BCA7FB212137E000753A3C /* IssueTargetBranchModel.swift */; }; + 29BCA7FF212137E100753A3C /* IssueBranchesSectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29BCA7FC212137E000753A3C /* IssueBranchesSectionController.swift */; }; + 29BCA800212137E100753A3C /* IssueTargetBranchCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29BCA7FD212137E000753A3C /* IssueTargetBranchCell.swift */; }; + 29BCA8052121384700753A3C /* ReactionsMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29BCA8042121384700753A3C /* ReactionsMenuViewController.swift */; }; 29BE40D32070786400A79C86 /* CMarkParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29BE40D22070786400A79C86 /* CMarkParsing.swift */; }; 29C0E7071ECBC6C50051D756 /* GithubClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29C0E7061ECBC6C50051D756 /* GithubClient.swift */; }; 29C167671ECA005500439D62 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29C167661ECA005500439D62 /* Constants.swift */; }; @@ -522,9 +523,6 @@ /* Begin PBXFileReference section */ 07C7DC5A7A907BE73BCA95AC /* Pods-FreetimeTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FreetimeTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-FreetimeTests/Pods-FreetimeTests.debug.xcconfig"; sourceTree = ""; }; 0F26A46A43D11F8D04E17D4A /* Pods-FreetimeWatch Extension.testflight.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FreetimeWatch Extension.testflight.xcconfig"; path = "Pods/Target Support Files/Pods-FreetimeWatch Extension/Pods-FreetimeWatch Extension.testflight.xcconfig"; sourceTree = ""; }; - 15245484205DC016005810A6 /* IssueTargetBranchModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = IssueTargetBranchModel.swift; path = Classes/Issues/Branches/IssueTargetBranchModel.swift; sourceTree = SOURCE_ROOT; }; - 15245485205DC016005810A6 /* IssueBranchesSectionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = IssueBranchesSectionController.swift; path = Classes/Issues/Branches/IssueBranchesSectionController.swift; sourceTree = SOURCE_ROOT; }; - 15245486205DC016005810A6 /* IssueTargetBranchCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = IssueTargetBranchCell.swift; path = Classes/Issues/Branches/IssueTargetBranchCell.swift; sourceTree = SOURCE_ROOT; }; 15F28F982108DBA6006421B6 /* SplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashView.swift; sourceTree = ""; }; 290056F2210028B20046EAE5 /* UIViewController+MenuDone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+MenuDone.swift"; sourceTree = ""; }; 2905AFAA1F7357B40015AE32 /* RepositoryOverviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepositoryOverviewViewController.swift; sourceTree = ""; }; @@ -849,6 +847,10 @@ 29BBD82820CAC7D5004D62FE /* NotificationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationViewModel.swift; sourceTree = ""; }; 29BBD82A20CACB2F004D62FE /* NotificationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCell.swift; sourceTree = ""; }; 29BBD82C20CACDFF004D62FE /* NotificationSectionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSectionController.swift; sourceTree = ""; }; + 29BCA7FB212137E000753A3C /* IssueTargetBranchModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IssueTargetBranchModel.swift; sourceTree = ""; }; + 29BCA7FC212137E000753A3C /* IssueBranchesSectionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IssueBranchesSectionController.swift; sourceTree = ""; }; + 29BCA7FD212137E000753A3C /* IssueTargetBranchCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IssueTargetBranchCell.swift; sourceTree = ""; }; + 29BCA8042121384700753A3C /* ReactionsMenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewController.swift; sourceTree = ""; }; 29BE40D22070786400A79C86 /* CMarkParsing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CMarkParsing.swift; sourceTree = ""; }; 29C0E7061ECBC6C50051D756 /* GithubClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GithubClient.swift; sourceTree = ""; }; 29C167661ECA005500439D62 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; @@ -1042,17 +1044,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 15245483205DC004005810A6 /* Branches */ = { - isa = PBXGroup; - children = ( - 15245485205DC016005810A6 /* IssueBranchesSectionController.swift */, - 15245486205DC016005810A6 /* IssueTargetBranchCell.swift */, - 15245484205DC016005810A6 /* IssueTargetBranchModel.swift */, - ); - name = Branches; - path = "New Group"; - sourceTree = ""; - }; 15F28F972108DB8C006421B6 /* SplashView */ = { isa = PBXGroup; children = ( @@ -1172,7 +1163,7 @@ children = ( 290744A81F24D2DA00FD9E48 /* AddCommentClient.swift */, 297403CF1F184F8400ABA95A /* Assignees */, - 15245483205DC004005810A6 /* Branches */, + 29BCA7FA212137E000753A3C /* Branches */, 292FCAC81EDFCC510026635E /* Comments */, 293A45A01F29953800DD1006 /* Commit */, 292CD3B91F0AF26900D3D57B /* DiffHunk */, @@ -1277,6 +1268,7 @@ 2908C5881F6F3EB00071C39D /* IssueLocalReaction.swift */, 292FCB201EDFCF870026635E /* IssueReactionCell.swift */, 29FF85A41EE1EA7A007B8762 /* ReactionContent+ReactionType.swift */, + 29BCA8042121384700753A3C /* ReactionsMenuViewController.swift */, 292FCB1C1EDFCD3D0026635E /* ReactionViewModel.swift */, 2963A9311EE1EBE20066509C /* UIMenuController+Reactions.swift */, ); @@ -1841,6 +1833,16 @@ path = EditComment; sourceTree = ""; }; + 29BCA7FA212137E000753A3C /* Branches */ = { + isa = PBXGroup; + children = ( + 29BCA7FB212137E000753A3C /* IssueTargetBranchModel.swift */, + 29BCA7FC212137E000753A3C /* IssueBranchesSectionController.swift */, + 29BCA7FD212137E000753A3C /* IssueTargetBranchCell.swift */, + ); + path = Branches; + sourceTree = ""; + }; 29C1677B1ECA1CED00439D62 /* Settings */ = { isa = PBXGroup; children = ( @@ -2723,7 +2725,7 @@ 29EB1EEF1F425E5100A200B4 /* ForegroundHandler.swift in Sources */, 29C167741ECA0DBB00439D62 /* GithubAPIDateFormatter.swift in Sources */, 294434E11FB1F2DA00050C06 /* BookmarkNavigationController.swift in Sources */, - 15245487205DC016005810A6 /* IssueTargetBranchModel.swift in Sources */, + 29BCA7FE212137E100753A3C /* IssueTargetBranchModel.swift in Sources */, 29EE44461F19D5C100B05ED3 /* GithubClient+Issues.swift in Sources */, 299F63E4205E1CAB0015D901 /* UIViewController+StyledTextViewCellDelegate.swift in Sources */, 29B94E6D1FCB472400715D7E /* IssueFileChangesModel.swift in Sources */, @@ -2942,6 +2944,7 @@ 29FF85A51EE1EA7A007B8762 /* ReactionContent+ReactionType.swift in Sources */, 296A4960210E7B9A00BBBF2B /* IssueDetailBadgeView.swift in Sources */, 292FCB1D1EDFCD3D0026635E /* ReactionViewModel.swift in Sources */, + 29BCA8052121384700753A3C /* ReactionsMenuViewController.swift in Sources */, 29DAA7AF20202BEA0029277A /* PullRequestReviewReplyModel.swift in Sources */, 29B94E691FCB36A000715D7E /* File+ListDiffable.swift in Sources */, 986B873E1F2E1CE400AAB55C /* RepositoryClient.swift in Sources */, @@ -3023,14 +3026,15 @@ 29C295111EC7B83200D46CD2 /* ShowMoreDetailsLabel.swift in Sources */, 292D2F1720FA5CAD00099342 /* Squawk+GitHawk.swift in Sources */, DC6339371F9F567000402A8D /* DeleteSwipeAction.swift in Sources */, + 29BCA7FF212137E100753A3C /* IssueBranchesSectionController.swift in Sources */, DCA5ED121FAEE3AE0072F074 /* Store.swift in Sources */, 29973E561F68BFDE0004B693 /* Signature.swift in Sources */, + 29BCA800212137E100753A3C /* IssueTargetBranchCell.swift in Sources */, 2971722D1F069E96005E43AC /* SpinnerCell.swift in Sources */, 296A4962210E7E0E00BBBF2B /* CheckIfSentWithGitHawk.swift in Sources */, 29B94E6F1FCB743900715D7E /* RepositoryFileCell.swift in Sources */, 29459A711FE7153500034A04 /* LogEnvironmentInformation.swift in Sources */, 49AF91B4204B4B6A00DFF325 /* MergeHelper.swift in Sources */, - 15245488205DC016005810A6 /* IssueBranchesSectionController.swift in Sources */, 2930F2731F8A27750082BA26 /* WidthCache.swift in Sources */, 29242814210A51B5001F5980 /* SpacerSectionController.swift in Sources */, 2971722B1F069E6B005E43AC /* SpinnerSectionController.swift in Sources */, @@ -3040,7 +3044,6 @@ 29C33FDB1F127DBB00EC8D40 /* SplitPlaceholderViewController.swift in Sources */, 2924C18320D5B2BF00FCFCFF /* MilestoneCell.swift in Sources */, 29C8F9AB208BF64D0075931C /* BaseListViewController2.swift in Sources */, - 15245489205DC016005810A6 /* IssueTargetBranchCell.swift in Sources */, 29AC90E51F00A7C8000B80E4 /* SplitViewControllerDelegate.swift in Sources */, 65A3152B2044376D0074E3B6 /* Route.swift in Sources */, 2977788A20B306F200F2AFC2 /* LabelLayoutManager.swift in Sources */, diff --git a/gql/API.swift b/gql/API.swift index 7cef6ebc7..4b5033d16 100644 --- a/gql/API.swift +++ b/gql/API.swift @@ -1500,7 +1500,7 @@ public final class IssueAutocompleteQuery: GraphQLQuery { public final class IssueOrPullRequestQuery: GraphQLQuery { public static let operationString = - "query IssueOrPullRequest($owner: String!, $repo: String!, $number: Int!, $page_size: Int!, $before: String) {\n repository(owner: $owner, name: $repo) {\n __typename\n name\n hasIssuesEnabled\n viewerCanAdminister\n mergeCommitAllowed\n rebaseMergeAllowed\n squashMergeAllowed\n mentionableUsers(first: 20) {\n __typename\n nodes {\n __typename\n avatarUrl\n login\n }\n }\n defaultBranchRef {\n __typename\n name\n }\n issueOrPullRequest(number: $number) {\n __typename\n ... on Issue {\n timeline(last: $page_size, before: $before) {\n __typename\n pageInfo {\n __typename\n ...headPaging\n }\n nodes {\n __typename\n ... on Commit {\n ...nodeFields\n author {\n __typename\n user {\n __typename\n login\n avatarUrl\n }\n }\n oid\n messageHeadline\n }\n ... on IssueComment {\n ...nodeFields\n ...reactionFields\n ...commentFields\n ...updatableFields\n ...deletableFields\n }\n ... on LabeledEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n label {\n __typename\n color\n name\n }\n createdAt\n }\n ... on UnlabeledEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n label {\n __typename\n color\n name\n }\n createdAt\n }\n ... on ClosedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n closer {\n __typename\n ... on Commit {\n oid\n }\n ... on PullRequest {\n mergeCommit {\n __typename\n oid\n }\n }\n }\n }\n ... on ReopenedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n }\n ... on RenamedTitleEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n currentTitle\n }\n ... on LockedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n }\n ... on UnlockedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n }\n ... on CrossReferencedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n source {\n __typename\n ... on Issue {\n title\n number\n closed\n repository {\n __typename\n name\n owner {\n __typename\n login\n }\n }\n }\n ... on PullRequest {\n title\n number\n closed\n merged\n repository {\n __typename\n name\n owner {\n __typename\n login\n }\n }\n }\n }\n }\n ... on ReferencedEvent {\n createdAt\n ...nodeFields\n refCommit: commit {\n __typename\n oid\n }\n actor {\n __typename\n login\n }\n commitRepository {\n __typename\n ...referencedRepositoryFields\n }\n subject {\n __typename\n ... on Issue {\n title\n number\n closed\n }\n ... on PullRequest {\n title\n number\n closed\n merged\n }\n }\n }\n ... on RenamedTitleEvent {\n ...nodeFields\n createdAt\n currentTitle\n previousTitle\n actor {\n __typename\n login\n }\n }\n ... on AssignedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n user {\n __typename\n login\n }\n }\n ... on UnassignedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n user {\n __typename\n login\n }\n }\n ... on MilestonedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n milestoneTitle\n }\n ... on DemilestonedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n milestoneTitle\n }\n }\n }\n milestone {\n __typename\n ...milestoneFields\n }\n ...reactionFields\n ...commentFields\n ...lockableFields\n ...closableFields\n ...labelableFields\n ...updatableFields\n ...nodeFields\n ...assigneeFields\n number\n title\n }\n ... on PullRequest {\n timeline(last: $page_size, before: $before) {\n __typename\n pageInfo {\n __typename\n ...headPaging\n }\n nodes {\n __typename\n ... on Commit {\n ...nodeFields\n author {\n __typename\n user {\n __typename\n login\n avatarUrl\n }\n }\n oid\n messageHeadline\n }\n ... on IssueComment {\n ...nodeFields\n ...reactionFields\n ...commentFields\n ...updatableFields\n ...deletableFields\n }\n ... on LabeledEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n label {\n __typename\n color\n name\n }\n createdAt\n }\n ... on UnlabeledEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n label {\n __typename\n color\n name\n }\n createdAt\n }\n ... on ClosedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n closer {\n __typename\n ... on Commit {\n oid\n }\n ... on PullRequest {\n mergeCommit {\n __typename\n oid\n }\n }\n }\n }\n ... on ReopenedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n }\n ... on RenamedTitleEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n currentTitle\n }\n ... on LockedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n }\n ... on UnlockedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n }\n ... on MergedEvent {\n ...nodeFields\n mergedCommit: commit {\n __typename\n oid\n }\n actor {\n __typename\n login\n }\n createdAt\n }\n ... on PullRequestReviewThread {\n comments(first: $page_size) {\n __typename\n nodes {\n __typename\n ...reactionFields\n ...nodeFields\n ...commentFields\n path\n diffHunk\n }\n }\n }\n ... on PullRequestReview {\n ...nodeFields\n ...commentFields\n state\n submittedAt\n author {\n __typename\n login\n }\n comments {\n __typename\n totalCount\n }\n }\n ... on CrossReferencedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n source {\n __typename\n ... on Issue {\n title\n number\n closed\n repository {\n __typename\n name\n owner {\n __typename\n login\n }\n }\n }\n ... on PullRequest {\n title\n number\n closed\n merged\n repository {\n __typename\n name\n owner {\n __typename\n login\n }\n }\n }\n }\n }\n ... on ReferencedEvent {\n createdAt\n ...nodeFields\n actor {\n __typename\n login\n }\n commitRepository {\n __typename\n ...referencedRepositoryFields\n }\n subject {\n __typename\n ... on Issue {\n title\n number\n closed\n }\n ... on PullRequest {\n title\n number\n closed\n merged\n }\n }\n }\n ... on RenamedTitleEvent {\n ...nodeFields\n createdAt\n currentTitle\n previousTitle\n actor {\n __typename\n login\n }\n }\n ... on AssignedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n user {\n __typename\n login\n }\n }\n ... on UnassignedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n user {\n __typename\n login\n }\n }\n ... on ReviewRequestedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n requestedReviewer {\n __typename\n ... on Actor {\n login\n }\n }\n }\n ... on ReviewRequestRemovedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n requestedReviewer {\n __typename\n ... on Actor {\n login\n }\n }\n }\n ... on MilestonedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n milestoneTitle\n }\n ... on DemilestonedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n milestoneTitle\n }\n }\n }\n reviewRequests(first: $page_size) {\n __typename\n nodes {\n __typename\n requestedReviewer {\n __typename\n ... on Actor {\n login\n avatarUrl\n }\n }\n }\n }\n commits(last: 1) {\n __typename\n nodes {\n __typename\n commit {\n __typename\n ...commitContext\n }\n }\n }\n milestone {\n __typename\n ...milestoneFields\n }\n ...reactionFields\n ...commentFields\n ...lockableFields\n ...closableFields\n ...labelableFields\n ...updatableFields\n ...nodeFields\n ...assigneeFields\n number\n title\n merged\n baseRefName\n changedFiles\n additions\n deletions\n mergeable\n mergeStateStatus\n }\n }\n }\n}" + "query IssueOrPullRequest($owner: String!, $repo: String!, $number: Int!, $page_size: Int!, $before: String) {\n repository(owner: $owner, name: $repo) {\n __typename\n name\n hasIssuesEnabled\n viewerCanAdminister\n mergeCommitAllowed\n rebaseMergeAllowed\n squashMergeAllowed\n mentionableUsers(first: 50) {\n __typename\n nodes {\n __typename\n avatarUrl\n login\n }\n }\n defaultBranchRef {\n __typename\n name\n }\n issueOrPullRequest(number: $number) {\n __typename\n ... on Issue {\n timeline(last: $page_size, before: $before) {\n __typename\n pageInfo {\n __typename\n ...headPaging\n }\n nodes {\n __typename\n ... on Commit {\n ...nodeFields\n author {\n __typename\n user {\n __typename\n login\n avatarUrl\n }\n }\n oid\n messageHeadline\n }\n ... on IssueComment {\n ...nodeFields\n ...reactionFields\n ...commentFields\n ...updatableFields\n ...deletableFields\n }\n ... on LabeledEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n label {\n __typename\n color\n name\n }\n createdAt\n }\n ... on UnlabeledEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n label {\n __typename\n color\n name\n }\n createdAt\n }\n ... on ClosedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n closer {\n __typename\n ... on Commit {\n oid\n }\n ... on PullRequest {\n mergeCommit {\n __typename\n oid\n }\n }\n }\n }\n ... on ReopenedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n }\n ... on RenamedTitleEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n currentTitle\n }\n ... on LockedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n }\n ... on UnlockedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n }\n ... on CrossReferencedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n source {\n __typename\n ... on Issue {\n title\n number\n closed\n repository {\n __typename\n name\n owner {\n __typename\n login\n }\n }\n }\n ... on PullRequest {\n title\n number\n closed\n merged\n repository {\n __typename\n name\n owner {\n __typename\n login\n }\n }\n }\n }\n }\n ... on ReferencedEvent {\n createdAt\n ...nodeFields\n refCommit: commit {\n __typename\n oid\n }\n actor {\n __typename\n login\n }\n commitRepository {\n __typename\n ...referencedRepositoryFields\n }\n subject {\n __typename\n ... on Issue {\n title\n number\n closed\n }\n ... on PullRequest {\n title\n number\n closed\n merged\n }\n }\n }\n ... on RenamedTitleEvent {\n ...nodeFields\n createdAt\n currentTitle\n previousTitle\n actor {\n __typename\n login\n }\n }\n ... on AssignedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n user {\n __typename\n login\n }\n }\n ... on UnassignedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n user {\n __typename\n login\n }\n }\n ... on MilestonedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n milestoneTitle\n }\n ... on DemilestonedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n milestoneTitle\n }\n }\n }\n milestone {\n __typename\n ...milestoneFields\n }\n ...reactionFields\n ...commentFields\n ...lockableFields\n ...closableFields\n ...labelableFields\n ...updatableFields\n ...nodeFields\n ...assigneeFields\n number\n title\n }\n ... on PullRequest {\n timeline(last: $page_size, before: $before) {\n __typename\n pageInfo {\n __typename\n ...headPaging\n }\n nodes {\n __typename\n ... on Commit {\n ...nodeFields\n author {\n __typename\n user {\n __typename\n login\n avatarUrl\n }\n }\n oid\n messageHeadline\n }\n ... on IssueComment {\n ...nodeFields\n ...reactionFields\n ...commentFields\n ...updatableFields\n ...deletableFields\n }\n ... on LabeledEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n label {\n __typename\n color\n name\n }\n createdAt\n }\n ... on UnlabeledEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n label {\n __typename\n color\n name\n }\n createdAt\n }\n ... on ClosedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n closer {\n __typename\n ... on Commit {\n oid\n }\n ... on PullRequest {\n mergeCommit {\n __typename\n oid\n }\n }\n }\n }\n ... on ReopenedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n }\n ... on RenamedTitleEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n currentTitle\n }\n ... on LockedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n }\n ... on UnlockedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n }\n ... on MergedEvent {\n ...nodeFields\n mergedCommit: commit {\n __typename\n oid\n }\n actor {\n __typename\n login\n }\n createdAt\n }\n ... on PullRequestReviewThread {\n comments(first: $page_size) {\n __typename\n nodes {\n __typename\n ...reactionFields\n ...nodeFields\n ...commentFields\n path\n diffHunk\n }\n }\n }\n ... on PullRequestReview {\n ...nodeFields\n ...commentFields\n state\n submittedAt\n author {\n __typename\n login\n }\n comments {\n __typename\n totalCount\n }\n }\n ... on CrossReferencedEvent {\n ...nodeFields\n actor {\n __typename\n login\n }\n createdAt\n source {\n __typename\n ... on Issue {\n title\n number\n closed\n repository {\n __typename\n name\n owner {\n __typename\n login\n }\n }\n }\n ... on PullRequest {\n title\n number\n closed\n merged\n repository {\n __typename\n name\n owner {\n __typename\n login\n }\n }\n }\n }\n }\n ... on ReferencedEvent {\n createdAt\n ...nodeFields\n actor {\n __typename\n login\n }\n commitRepository {\n __typename\n ...referencedRepositoryFields\n }\n subject {\n __typename\n ... on Issue {\n title\n number\n closed\n }\n ... on PullRequest {\n title\n number\n closed\n merged\n }\n }\n }\n ... on RenamedTitleEvent {\n ...nodeFields\n createdAt\n currentTitle\n previousTitle\n actor {\n __typename\n login\n }\n }\n ... on AssignedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n user {\n __typename\n login\n }\n }\n ... on UnassignedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n user {\n __typename\n login\n }\n }\n ... on ReviewRequestedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n requestedReviewer {\n __typename\n ... on Actor {\n login\n }\n }\n }\n ... on ReviewRequestRemovedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n requestedReviewer {\n __typename\n ... on Actor {\n login\n }\n }\n }\n ... on MilestonedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n milestoneTitle\n }\n ... on DemilestonedEvent {\n ...nodeFields\n createdAt\n actor {\n __typename\n login\n }\n milestoneTitle\n }\n }\n }\n reviewRequests(first: $page_size) {\n __typename\n nodes {\n __typename\n requestedReviewer {\n __typename\n ... on Actor {\n login\n avatarUrl\n }\n }\n }\n }\n commits(last: 1) {\n __typename\n nodes {\n __typename\n commit {\n __typename\n ...commitContext\n }\n }\n }\n milestone {\n __typename\n ...milestoneFields\n }\n ...reactionFields\n ...commentFields\n ...lockableFields\n ...closableFields\n ...labelableFields\n ...updatableFields\n ...nodeFields\n ...assigneeFields\n number\n title\n merged\n baseRefName\n changedFiles\n additions\n deletions\n mergeable\n mergeStateStatus\n }\n }\n }\n}" public static var requestString: String { return operationString.appending(HeadPaging.fragmentString).appending(NodeFields.fragmentString).appending(ReactionFields.fragmentString).appending(CommentFields.fragmentString).appending(UpdatableFields.fragmentString).appending(DeletableFields.fragmentString).appending(ReferencedRepositoryFields.fragmentString).appending(MilestoneFields.fragmentString).appending(LockableFields.fragmentString).appending(ClosableFields.fragmentString).appending(LabelableFields.fragmentString).appending(AssigneeFields.fragmentString).appending(CommitContext.fragmentString) } @@ -1560,7 +1560,7 @@ public final class IssueOrPullRequestQuery: GraphQLQuery { GraphQLField("mergeCommitAllowed", type: .nonNull(.scalar(Bool.self))), GraphQLField("rebaseMergeAllowed", type: .nonNull(.scalar(Bool.self))), GraphQLField("squashMergeAllowed", type: .nonNull(.scalar(Bool.self))), - GraphQLField("mentionableUsers", arguments: ["first": 20], type: .nonNull(.object(MentionableUser.selections))), + GraphQLField("mentionableUsers", arguments: ["first": 50], type: .nonNull(.object(MentionableUser.selections))), GraphQLField("defaultBranchRef", type: .object(DefaultBranchRef.selections)), GraphQLField("issueOrPullRequest", arguments: ["number": GraphQLVariable("number")], type: .object(IssueOrPullRequest.selections)), ]