diff --git a/Classes/Issues/Comments/Details/IssueCommentDetailCell.swift b/Classes/Issues/Comments/Details/IssueCommentDetailCell.swift index 54ba5cbff..81e814d0d 100644 --- a/Classes/Issues/Comments/Details/IssueCommentDetailCell.swift +++ b/Classes/Issues/Comments/Details/IssueCommentDetailCell.swift @@ -24,6 +24,7 @@ final class IssueCommentDetailCell: IssueCommentBaseCell, ListBindable { private let loginLabel = UILabel() private let dateLabel = ShowMoreDetailsLabel() private let editedLabel = ShowMoreDetailsLabel() + private let badgeView = IssueDetailBadgeView() private var login = "" override init(frame: CGRect) { @@ -72,6 +73,12 @@ final class IssueCommentDetailCell: IssueCommentBaseCell, ListBindable { make.left.equalTo(loginLabel.snp.right).offset(Styles.Sizes.columnSpacing/2) make.centerY.equalTo(loginLabel) } + + contentView.addSubview(badgeView) + badgeView.snp.makeConstraints { make in + make.centerY.equalTo(dateLabel) + make.right.equalTo(dateLabel.snp.left).offset(-Styles.Sizes.columnSpacing) + } } required init?(coder aDecoder: NSCoder) { @@ -96,6 +103,7 @@ final class IssueCommentDetailCell: IssueCommentBaseCell, ListBindable { imageView.sd_setImage(with: viewModel.avatarURL) dateLabel.setText(date: viewModel.date, format: .short) loginLabel.text = viewModel.login + badgeView.isHidden = !viewModel.sentWithGitHawk if let editedLogin = viewModel.editedBy, let editedDate = viewModel.editedAt { editedLabel.isHidden = false diff --git a/Classes/Issues/Comments/Details/IssueCommentDetailsViewModel.swift b/Classes/Issues/Comments/Details/IssueCommentDetailsViewModel.swift index 44c2dd4d5..ef444e385 100644 --- a/Classes/Issues/Comments/Details/IssueCommentDetailsViewModel.swift +++ b/Classes/Issues/Comments/Details/IssueCommentDetailsViewModel.swift @@ -17,6 +17,7 @@ final class IssueCommentDetailsViewModel: ListDiffable { let didAuthor: Bool let editedBy: String? let editedAt: Date? + let sentWithGitHawk: Bool init( date: Date, @@ -24,7 +25,8 @@ final class IssueCommentDetailsViewModel: ListDiffable { avatarURL: URL, didAuthor: Bool, editedBy: String?, - editedAt: Date? + editedAt: Date?, + sentWithGitHawk: Bool ) { self.date = date self.login = login @@ -32,6 +34,7 @@ final class IssueCommentDetailsViewModel: ListDiffable { self.didAuthor = didAuthor self.editedBy = editedBy self.editedAt = editedAt + self.sentWithGitHawk = sentWithGitHawk } func diffIdentifier() -> NSObjectProtocol { diff --git a/Classes/Issues/Comments/Details/IssueDetailBadgeView.swift b/Classes/Issues/Comments/Details/IssueDetailBadgeView.swift new file mode 100644 index 000000000..a4c4cdf89 --- /dev/null +++ b/Classes/Issues/Comments/Details/IssueDetailBadgeView.swift @@ -0,0 +1,53 @@ +// +// IssueDetailBadgeView.swift +// Freetime +// +// Created by Ryan Nystrom on 7/29/18. +// Copyright © 2018 Ryan Nystrom. All rights reserved. +// + +import UIKit + +final class IssueDetailBadgeView: UIImageView { + + init() { + super.init(frame: .zero) + image = UIImage(named: "githawk-badge")?.withRenderingMode(.alwaysTemplate) + tintColor = Styles.Colors.Blue.medium.color + + isUserInteractionEnabled = true + + let tap = UITapGestureRecognizer( + target: self, + action: #selector(ShowMoreDetailsLabel.showMenu(recognizer:)) + ) + addGestureRecognizer(tap) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override var canBecomeFirstResponder: Bool { + return true + } + + // MARK: Private API + + @objc func showMenu(recognizer: UITapGestureRecognizer) { + becomeFirstResponder() + + let menu = UIMenuController.shared + menu.menuItems = [ + UIMenuItem( + title: NSLocalizedString("Sent with GitHawk", comment: ""), + action: #selector(IssueDetailBadgeView.empty) + ) + ] + menu.setTargetRect(bounds, in: self) + menu.setMenuVisible(true, animated: trueUnlessReduceMotionEnabled) + } + + @objc func empty() {} + +} diff --git a/Classes/Issues/Comments/Markdown/CheckIfSentWithGitHawk.swift b/Classes/Issues/Comments/Markdown/CheckIfSentWithGitHawk.swift new file mode 100644 index 000000000..11cce0ca2 --- /dev/null +++ b/Classes/Issues/Comments/Markdown/CheckIfSentWithGitHawk.swift @@ -0,0 +1,18 @@ +// +// CheckIfSentWithGitHawk.swift +// Freetime +// +// Created by Ryan Nystrom on 7/29/18. +// Copyright © 2018 Ryan Nystrom. All rights reserved. +// + +import Foundation + +func CheckIfSentWithGitHawk(markdown: String) -> (sentWithGitHawk: Bool, markdown: String) { + let nsstring = markdown as NSString + let range = nsstring.range(of: Signature.signature, options: .backwards) + if range.location != NSNotFound && range.location + range.length == nsstring.length { + return (true, nsstring.replacingCharacters(in: range, with: "")) + } + return (false, markdown) +} diff --git a/Classes/Issues/IssueViewModels.swift b/Classes/Issues/IssueViewModels.swift index 0db80266c..0d20d4bb5 100644 --- a/Classes/Issues/IssueViewModels.swift +++ b/Classes/Issues/IssueViewModels.swift @@ -65,17 +65,20 @@ func createCommentModel( let avatarURL = URL(string: author.avatarUrl) else { return nil } + let checkedMarkdown = CheckIfSentWithGitHawk(markdown: commentFields.body) + let details = IssueCommentDetailsViewModel( date: date, login: author.login, avatarURL: avatarURL, didAuthor: commentFields.viewerDidAuthor, editedBy: commentFields.editor?.login, - editedAt: commentFields.lastEditedAt?.githubDate + editedAt: commentFields.lastEditedAt?.githubDate, + sentWithGitHawk: checkedMarkdown.sentWithGitHawk ) let bodies = MarkdownModels( - commentFields.body, + checkedMarkdown.markdown, owner: owner, repo: repo, width: width, diff --git a/Classes/PullRequestReviews/GithubClient+PullRequestReviewComments.swift b/Classes/PullRequestReviews/GithubClient+PullRequestReviewComments.swift index 6b2003fa1..8bd57ce67 100644 --- a/Classes/PullRequestReviews/GithubClient+PullRequestReviewComments.swift +++ b/Classes/PullRequestReviews/GithubClient+PullRequestReviewComments.swift @@ -177,18 +177,21 @@ private func createReviewComment( contentSizeCategory: UIContentSizeCategory, width: CGFloat ) -> IssueCommentModel { + let checkedMarkdown = CheckIfSentWithGitHawk(markdown: model.body) + let details = IssueCommentDetailsViewModel( date: model.created, login: model.author, avatarURL: model.authorAvatarURL, didAuthor: model.author == viewer, editedBy: nil, - editedAt: nil + editedAt: nil, + sentWithGitHawk: checkedMarkdown.sentWithGitHawk ) let reactions = IssueCommentReactionViewModel(models: []) let bodies = MarkdownModels( - model.body, + checkedMarkdown.markdown, owner: owner, repo: repo, width: width, diff --git a/Classes/Systems/Signature.swift b/Classes/Systems/Signature.swift index d96798ac3..d219432f1 100644 --- a/Classes/Systems/Signature.swift +++ b/Classes/Systems/Signature.swift @@ -22,11 +22,15 @@ enum Signature { } } - static func signed(text: String) -> String { - guard enabled else { return text } + static var signature: String { let format = NSLocalizedString("Sent with %@", comment: "") let signature = String(format: format, "GitHawk") - return text + "\n\n\(signature)" + return "\n\n\(signature)" + } + + static func signed(text: String) -> String { + guard enabled else { return text } + return text + signature } } diff --git a/Classes/Views/ShowMoreDetailsLabel.swift b/Classes/Views/ShowMoreDetailsLabel.swift index b594e96c4..bab4999c8 100644 --- a/Classes/Views/ShowMoreDetailsLabel.swift +++ b/Classes/Views/ShowMoreDetailsLabel.swift @@ -34,7 +34,7 @@ final class ShowMoreDetailsLabel: UILabel { // MARK: Private API - @objc func showMenu(recognizer: UITapGestureRecognizer) { + @objc func showMenu(recognizer: UILongPressGestureRecognizer) { guard recognizer.state == .began, !detailText.isEmpty else { return } diff --git a/Freetime.xcodeproj/project.pbxproj b/Freetime.xcodeproj/project.pbxproj index f18abe4ce..52360a9fc 100644 --- a/Freetime.xcodeproj/project.pbxproj +++ b/Freetime.xcodeproj/project.pbxproj @@ -206,6 +206,8 @@ 2965F3702071508C003CC92F /* StyledTextBuilder+Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2965F36F2071508C003CC92F /* StyledTextBuilder+Checkbox.swift */; }; 2965F37220715161003CC92F /* StyledTextBuilder+NewBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2965F37120715161003CC92F /* StyledTextBuilder+NewBase.swift */; }; 29693EE520FAA05F00336200 /* IssueAutocomplete.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29693EE420FAA05F00336200 /* IssueAutocomplete.swift */; }; + 296A4960210E7B9A00BBBF2B /* IssueDetailBadgeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 296A495F210E7B9A00BBBF2B /* IssueDetailBadgeView.swift */; }; + 296A4962210E7E0E00BBBF2B /* CheckIfSentWithGitHawk.swift in Sources */ = {isa = PBXBuildFile; fileRef = 296A4961210E7E0E00BBBF2B /* CheckIfSentWithGitHawk.swift */; }; 296B4E311F7C805600C16887 /* GraphQLIDDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 296B4E301F7C805600C16887 /* GraphQLIDDecode.swift */; }; 296B4E341F7C80B800C16887 /* GraphQLIDDecodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 296B4E331F7C80B800C16887 /* GraphQLIDDecodeTests.swift */; }; 2971722B1F069E6B005E43AC /* SpinnerSectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2971722A1F069E6B005E43AC /* SpinnerSectionController.swift */; }; @@ -715,6 +717,8 @@ 2965F36F2071508C003CC92F /* StyledTextBuilder+Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StyledTextBuilder+Checkbox.swift"; sourceTree = ""; }; 2965F37120715161003CC92F /* StyledTextBuilder+NewBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StyledTextBuilder+NewBase.swift"; sourceTree = ""; }; 29693EE420FAA05F00336200 /* IssueAutocomplete.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueAutocomplete.swift; sourceTree = ""; }; + 296A495F210E7B9A00BBBF2B /* IssueDetailBadgeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IssueDetailBadgeView.swift; sourceTree = ""; }; + 296A4961210E7E0E00BBBF2B /* CheckIfSentWithGitHawk.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckIfSentWithGitHawk.swift; sourceTree = ""; }; 296B4E301F7C805600C16887 /* GraphQLIDDecode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLIDDecode.swift; sourceTree = ""; }; 296B4E331F7C80B800C16887 /* GraphQLIDDecodeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLIDDecodeTests.swift; sourceTree = ""; }; 2971722A1F069E6B005E43AC /* SpinnerSectionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpinnerSectionController.swift; sourceTree = ""; }; @@ -1239,6 +1243,7 @@ 292FCACD1EDFCC510026635E /* Details */ = { isa = PBXGroup; children = ( + 296A495F210E7B9A00BBBF2B /* IssueDetailBadgeView.swift */, 292FCACE1EDFCC510026635E /* IssueCommentDetailCell.swift */, 292FCACF1EDFCC510026635E /* IssueCommentDetailsViewModel.swift */, ); @@ -1464,6 +1469,7 @@ 2965F37120715161003CC92F /* StyledTextBuilder+NewBase.swift */, 29921BCB1EF624D400C1E848 /* UIFont+MutableTraits.swift */, 2977788720B0DAD500F2AFC2 /* ViewMarkdownViewController.swift */, + 296A4961210E7E0E00BBBF2B /* CheckIfSentWithGitHawk.swift */, ); path = Markdown; sourceTree = ""; @@ -2919,6 +2925,7 @@ 29136BDB200A626D007317BE /* FixedRefreshControl.swift in Sources */, 2980033E1F51E93500BE90F4 /* RatingSectionController.swift in Sources */, 29FF85A51EE1EA7A007B8762 /* ReactionContent+ReactionType.swift in Sources */, + 296A4960210E7B9A00BBBF2B /* IssueDetailBadgeView.swift in Sources */, 292FCB1D1EDFCD3D0026635E /* ReactionViewModel.swift in Sources */, 29DAA7AF20202BEA0029277A /* PullRequestReviewReplyModel.swift in Sources */, 29B94E691FCB36A000715D7E /* File+ListDiffable.swift in Sources */, @@ -3003,6 +3010,7 @@ DCA5ED121FAEE3AE0072F074 /* Store.swift in Sources */, 29973E561F68BFDE0004B693 /* Signature.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 */, diff --git a/Resources/Assets.xcassets/githawk-badge.imageset/Contents.json b/Resources/Assets.xcassets/githawk-badge.imageset/Contents.json new file mode 100644 index 000000000..07370ac7a --- /dev/null +++ b/Resources/Assets.xcassets/githawk-badge.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "githawk-badge@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "githawk-badge@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Resources/Assets.xcassets/githawk-badge.imageset/githawk-badge@2x.png b/Resources/Assets.xcassets/githawk-badge.imageset/githawk-badge@2x.png new file mode 100644 index 000000000..788c429dd Binary files /dev/null and b/Resources/Assets.xcassets/githawk-badge.imageset/githawk-badge@2x.png differ diff --git a/Resources/Assets.xcassets/githawk-badge.imageset/githawk-badge@3x.png b/Resources/Assets.xcassets/githawk-badge.imageset/githawk-badge@3x.png new file mode 100644 index 000000000..28e0f6b50 Binary files /dev/null and b/Resources/Assets.xcassets/githawk-badge.imageset/githawk-badge@3x.png differ