diff --git a/Classes/Issues/Issue+IssueType.swift b/Classes/Issues/Issue+IssueType.swift index 21844629..d793eabd 100644 --- a/Classes/Issues/Issue+IssueType.swift +++ b/Classes/Issues/Issue+IssueType.swift @@ -85,6 +85,7 @@ extension IssueOrPullRequestQuery.Data.Repository.IssueOrPullRequest.AsIssue: Is let model = IssueStatusEventModel( id: closed.fragments.nodeFields.id, actor: closed.actor?.login ?? Strings.unknown, + commitHash: closed.closedCommit?.oid, date: date, status: .closed, pullRequest: false @@ -95,6 +96,7 @@ extension IssueOrPullRequestQuery.Data.Repository.IssueOrPullRequest.AsIssue: Is let model = IssueStatusEventModel( id: reopened.fragments.nodeFields.id, actor: reopened.actor?.login ?? Strings.unknown, + commitHash: nil, date: date, status: .reopened, pullRequest: false @@ -105,6 +107,7 @@ extension IssueOrPullRequestQuery.Data.Repository.IssueOrPullRequest.AsIssue: Is let model = IssueStatusEventModel( id: locked.fragments.nodeFields.id, actor: locked.actor?.login ?? Strings.unknown, + commitHash: nil, date: date, status: .locked, pullRequest: false @@ -115,6 +118,7 @@ extension IssueOrPullRequestQuery.Data.Repository.IssueOrPullRequest.AsIssue: Is let model = IssueStatusEventModel( id: unlocked.fragments.nodeFields.id, actor: unlocked.actor?.login ?? Strings.unknown, + commitHash: nil, date: date, status: .unlocked, pullRequest: false diff --git a/Classes/Issues/IssuesViewController.swift b/Classes/Issues/IssuesViewController.swift index 80655431..822cf71b 100644 --- a/Classes/Issues/IssuesViewController.swift +++ b/Classes/Issues/IssuesViewController.swift @@ -99,8 +99,7 @@ final class IssuesViewController: UIViewController, ListAdapterDataSource, FeedD case is IssueLabelsModel: return IssueLabelsSectionController() case is IssueStatusModel: return IssueStatusSectionController() case is IssueLabeledModel: return IssueLabeledSectionController(owner: owner, repo: repo) - case is IssueStatusEventModel: return IssueStatusEventSectionController() - case is IssueMergedModel: return IssueMergedSectionController(owner: owner, repo: repo) + case is IssueStatusEventModel: return IssueStatusEventSectionController(owner: owner, repo: repo) case is IssueDiffHunkModel: return IssueDiffHunkSectionController() case is IssueReviewModel: return IssueReviewSectionController() case is IssueReferencedModel: return IssueReferencedSectionController(client: client) diff --git a/Classes/Issues/Merged/IssueMergedCell.swift b/Classes/Issues/Merged/IssueMergedCell.swift deleted file mode 100644 index 474972c0..00000000 --- a/Classes/Issues/Merged/IssueMergedCell.swift +++ /dev/null @@ -1,87 +0,0 @@ -// -// IssueMergedCell.swift -// Freetime -// -// Created by Ryan Nystrom on 6/29/17. -// Copyright © 2017 Ryan Nystrom. All rights reserved. -// - -import UIKit -import SnapKit - -protocol IssueMergedCellDelegate: class { - func didTapActor(cell: IssueMergedCell) - func didTapHash(cell: IssueMergedCell) -} - -final class IssueMergedCell: UICollectionViewCell { - - weak var delegate: IssueMergedCellDelegate? = nil - - private let actorButton = UIButton() - private let hashButton = UIButton() - private let mergedButton = UIButton() - private let dateLabel = ShowMoreDetailsLabel() - - override init(frame: CGRect) { - super.init(frame: frame) - - actorButton.titleLabel?.font = Styles.Fonts.bodyBold - actorButton.setTitleColor(Styles.Colors.Gray.dark.color, for: .normal) - actorButton.addTarget(self, action: #selector(IssueMergedCell.onActor), for: .touchUpInside) - contentView.addSubview(actorButton) - actorButton.snp.makeConstraints { make in - make.left.equalTo(Styles.Sizes.gutter) - make.centerY.equalTo(contentView) - } - - mergedButton.setTitle(Strings.merged, for: .normal) - mergedButton.setupAsLabel() - mergedButton.config(pullRequest: false, state: .merged) - contentView.addSubview(mergedButton) - mergedButton.snp.makeConstraints { make in - make.left.equalTo(actorButton.snp.right).offset(Styles.Sizes.inlineSpacing) - make.centerY.equalTo(contentView) - } - - hashButton.titleLabel?.font = UIFont(name: "Courier-Bold", size: Styles.Sizes.Text.body) - hashButton.setTitleColor(Styles.Colors.Gray.dark.color, for: .normal) - hashButton.addTarget(self, action: #selector(IssueMergedCell.onHash), for: .touchUpInside) - contentView.addSubview(hashButton) - hashButton.snp.makeConstraints { make in - make.left.equalTo(mergedButton.snp.right).offset(Styles.Sizes.inlineSpacing) - make.centerY.equalTo(contentView) - } - - dateLabel.font = Styles.Fonts.body - dateLabel.textColor = Styles.Colors.Gray.medium.color - dateLabel.backgroundColor = .clear - contentView.addSubview(dateLabel) - dateLabel.snp.makeConstraints { make in - make.left.equalTo(hashButton.snp.right).offset(Styles.Sizes.inlineSpacing) - make.centerY.equalTo(contentView) - } - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: Private API - - func onActor() { - delegate?.didTapActor(cell: self) - } - - func onHash() { - delegate?.didTapHash(cell: self) - } - - // MARK: Public API - - func configure(viewModel: IssueMergedModel) { - actorButton.setTitle(viewModel.actor, for: .normal) - dateLabel.setText(date: viewModel.date) - hashButton.setTitle(viewModel.commitHash.hashDisplay, for: .normal) - } -} diff --git a/Classes/Issues/Merged/IssueMergedModel.swift b/Classes/Issues/Merged/IssueMergedModel.swift deleted file mode 100644 index 09a364bb..00000000 --- a/Classes/Issues/Merged/IssueMergedModel.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// IssueMergedModel.swift -// Freetime -// -// Created by Ryan Nystrom on 6/29/17. -// Copyright © 2017 Ryan Nystrom. All rights reserved. -// - -import Foundation -import IGListKit - -final class IssueMergedModel: ListDiffable { - - let date: Date - let commitHash: String - let actor: String - - init(date: Date, commitHash: String, actor: String) { - self.date = date - self.commitHash = commitHash - self.actor = actor - } - - // MARK: ListDiffable - - func diffIdentifier() -> NSObjectProtocol { - return commitHash as NSObjectProtocol - } - - func isEqual(toDiffableObject object: ListDiffable?) -> Bool { - return true - } - -} diff --git a/Classes/Issues/Merged/IssueMergedSectionController.swift b/Classes/Issues/Merged/IssueMergedSectionController.swift deleted file mode 100644 index 8f063d4c..00000000 --- a/Classes/Issues/Merged/IssueMergedSectionController.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// IssueMergedSectionController.swift -// Freetime -// -// Created by Ryan Nystrom on 6/29/17. -// Copyright © 2017 Ryan Nystrom. All rights reserved. -// - -import UIKit -import IGListKit - -final class IssueMergedSectionController: ListGenericSectionController, IssueMergedCellDelegate { - - private let owner: String - private let repo: String - - init(owner: String, repo: String) { - self.owner = owner - self.repo = repo - super.init() - inset = Styles.Sizes.listInsetTight - } - - override func sizeForItem(at index: Int) -> CGSize { - guard let width = collectionContext?.containerSize.width else { fatalError("Collection context must be set") } - return CGSize(width: width, height: Styles.Sizes.labelEventHeight) - } - - override func cellForItem(at index: Int) -> UICollectionViewCell { - guard let cell = collectionContext?.dequeueReusableCell(of: IssueMergedCell.self, for: self, at: index) as? IssueMergedCell, - let object = self.object - else { fatalError("Missing context, object, or cell wrong type") } - cell.configure(viewModel: object) - cell.delegate = self - return cell - } - - // MARK: IssueMergedCellDelegate - - func didTapActor(cell: IssueMergedCell) { - guard let actor = object?.actor else { return } - viewController?.presentProfile(login: actor) - } - - func didTapHash(cell: IssueMergedCell) { - guard let hash = object?.commitHash else { return } - viewController?.presentCommit(owner: owner, repo: repo, hash: hash) - } - -} diff --git a/Classes/Issues/PullRequest+IssueType.swift b/Classes/Issues/PullRequest+IssueType.swift index 647f7f34..33ee5abe 100644 --- a/Classes/Issues/PullRequest+IssueType.swift +++ b/Classes/Issues/PullRequest+IssueType.swift @@ -82,6 +82,7 @@ extension IssueOrPullRequestQuery.Data.Repository.IssueOrPullRequest.AsPullReque let model = IssueStatusEventModel( id: closed.fragments.nodeFields.id, actor: closed.actor?.login ?? Strings.unknown, + commitHash: closed.closedCommit?.oid, date: date, status: .closed, pullRequest: true @@ -92,6 +93,7 @@ extension IssueOrPullRequestQuery.Data.Repository.IssueOrPullRequest.AsPullReque let model = IssueStatusEventModel( id: reopened.fragments.nodeFields.id, actor: reopened.actor?.login ?? Strings.unknown, + commitHash: nil, date: date, status: .reopened, pullRequest: true @@ -99,10 +101,13 @@ extension IssueOrPullRequestQuery.Data.Repository.IssueOrPullRequest.AsPullReque results.append(model) } else if let merged = node.asMergedEvent, let date = GithubAPIDateFormatter().date(from: merged.createdAt) { - let model = IssueMergedModel( + let model = IssueStatusEventModel( + id: merged.fragments.nodeFields.id, + actor: merged.actor?.login ?? Strings.unknown, + commitHash: merged.mergedCommit.oid, date: date, - commitHash: merged.commit.oid, - actor: merged.actor?.login ?? Strings.unknown + status: .merged, + pullRequest: true ) results.append(model) } else if let locked = node.asLockedEvent, @@ -110,6 +115,7 @@ extension IssueOrPullRequestQuery.Data.Repository.IssueOrPullRequest.AsPullReque let model = IssueStatusEventModel( id: locked.fragments.nodeFields.id, actor: locked.actor?.login ?? Strings.unknown, + commitHash: nil, date: date, status: .locked, pullRequest: false @@ -120,6 +126,7 @@ extension IssueOrPullRequestQuery.Data.Repository.IssueOrPullRequest.AsPullReque let model = IssueStatusEventModel( id: unlocked.fragments.nodeFields.id, actor: unlocked.actor?.login ?? Strings.unknown, + commitHash: nil, date: date, status: .unlocked, pullRequest: false diff --git a/Classes/Issues/StatusEvent/IssueStatusEventCell.swift b/Classes/Issues/StatusEvent/IssueStatusEventCell.swift index 42ff7e41..ecefbfdc 100644 --- a/Classes/Issues/StatusEvent/IssueStatusEventCell.swift +++ b/Classes/Issues/StatusEvent/IssueStatusEventCell.swift @@ -11,6 +11,7 @@ import SnapKit protocol IssueStatusEventCellDelegate: class { func didTapActor(cell: IssueStatusEventCell) + func didTapHash(cell: IssueStatusEventCell) } final class IssueStatusEventCell: UICollectionViewCell { @@ -18,9 +19,12 @@ final class IssueStatusEventCell: UICollectionViewCell { weak var delegate: IssueStatusEventCellDelegate? = nil private let actorButton = UIButton() - private let button = UIButton() + private let hashButton = UIButton() + private let statusButton = UIButton() private let dateLabel = ShowMoreDetailsLabel() + private var dateConstraint: Constraint? = nil + override init(frame: CGRect) { super.init(frame: frame) @@ -31,19 +35,28 @@ final class IssueStatusEventCell: UICollectionViewCell { make.centerY.equalTo(contentView) } - button.setupAsLabel() - contentView.addSubview(button) - button.snp.makeConstraints { make in + statusButton.setupAsLabel() + contentView.addSubview(statusButton) + statusButton.snp.makeConstraints { make in make.left.equalTo(actorButton.snp.right).offset(Styles.Sizes.inlineSpacing) make.centerY.equalTo(contentView) } + hashButton.titleLabel?.font = UIFont(name: "Courier-Bold", size: Styles.Sizes.Text.body) + hashButton.setTitleColor(Styles.Colors.Gray.dark.color, for: .normal) + hashButton.addTarget(self, action: #selector(IssueStatusEventCell.onHash), for: .touchUpInside) + contentView.addSubview(hashButton) + hashButton.snp.makeConstraints { make in + make.left.equalTo(statusButton.snp.right).offset(Styles.Sizes.inlineSpacing) + make.centerY.equalTo(contentView) + } + dateLabel.font = Styles.Fonts.body dateLabel.textColor = Styles.Colors.Gray.medium.color dateLabel.backgroundColor = .clear contentView.addSubview(dateLabel) dateLabel.snp.makeConstraints { make in - make.left.equalTo(button.snp.right).offset(Styles.Sizes.inlineSpacing) + self.dateConstraint = make.left.equalTo(hashButton.snp.right).offset(Styles.Sizes.inlineSpacing).constraint make.centerY.equalTo(contentView) } } @@ -58,6 +71,10 @@ final class IssueStatusEventCell: UICollectionViewCell { delegate?.didTapActor(cell: self) } + func onHash() { + delegate?.didTapHash(cell: self) + } + // MARK: Public API func configure(_ model: IssueStatusEventModel) { @@ -67,18 +84,21 @@ final class IssueStatusEventCell: UICollectionViewCell { ] actorButton.setAttributedTitle(NSAttributedString(string: model.actor, attributes: actorAttributes), for: .normal) - button.config(pullRequest: model.pullRequest, state: model.status.buttonState) - let title: String switch model.status { case .reopened: title = Strings.reopened // open event only happens when RE-opening case .closed: title = Strings.closed case .locked: title = Strings.locked case .unlocked: title = NSLocalizedString("Unlocked", comment: "") - case .merged: fatalError("Merge events handled in other model+cell") + case .merged: title = Strings.merged } - button.setTitle(title, for: .normal) + statusButton.setTitle(title, for: .normal) + statusButton.config(pullRequest: model.pullRequest, state: model.status.buttonState) + let hash = model.commitHash?.hashDisplay + hashButton.setTitle(hash, for: .normal) + + dateConstraint?.update(offset: hash == nil ? -30 : Styles.Sizes.inlineSpacing) dateLabel.setText(date: model.date) } diff --git a/Classes/Issues/StatusEvent/IssueStatusEventModel.swift b/Classes/Issues/StatusEvent/IssueStatusEventModel.swift index 70300918..d1ca0828 100644 --- a/Classes/Issues/StatusEvent/IssueStatusEventModel.swift +++ b/Classes/Issues/StatusEvent/IssueStatusEventModel.swift @@ -13,13 +13,15 @@ final class IssueStatusEventModel: ListDiffable { let id: String let actor: String + let commitHash: String? let date: Date let status: IssueStatusEvent let pullRequest: Bool - init(id: String, actor: String, date: Date, status: IssueStatusEvent, pullRequest: Bool) { + init(id: String, actor: String, commitHash: String?, date: Date, status: IssueStatusEvent, pullRequest: Bool) { self.id = id self.actor = actor + self.commitHash = commitHash self.date = date self.status = status self.pullRequest = pullRequest diff --git a/Classes/Issues/StatusEvent/IssueStatusEventSectionController.swift b/Classes/Issues/StatusEvent/IssueStatusEventSectionController.swift index 1b4f2ee5..f8fc2680 100644 --- a/Classes/Issues/StatusEvent/IssueStatusEventSectionController.swift +++ b/Classes/Issues/StatusEvent/IssueStatusEventSectionController.swift @@ -11,9 +11,14 @@ import IGListKit final class IssueStatusEventSectionController: ListGenericSectionController, IssueStatusEventCellDelegate { - override init() { + private let owner: String + private let repo: String + + init(owner: String, repo: String) { + self.owner = owner + self.repo = repo super.init() - self.inset = Styles.Sizes.listInsetTight + inset = Styles.Sizes.listInsetTight } override func sizeForItem(at index: Int) -> CGSize { @@ -37,5 +42,10 @@ final class IssueStatusEventSectionController: ListGenericSectionController CFBundleVersion - 766 + 773 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/gql/API.swift b/gql/API.swift index e85168d5..6887e1af 100644 --- a/gql/API.swift +++ b/gql/API.swift @@ -148,6 +148,10 @@ public final class IssueOrPullRequestQuery: GraphQLQuery { " ... on ClosedEvent {" + " __typename" + " ...nodeFields" + + " closedCommit: commit {" + + " __typename" + + " oid" + + " }" + " actor {" + " __typename" + " login" + @@ -325,6 +329,10 @@ public final class IssueOrPullRequestQuery: GraphQLQuery { " ... on ClosedEvent {" + " __typename" + " ...nodeFields" + + " closedCommit: commit {" + + " __typename" + + " oid" + + " }" + " actor {" + " __typename" + " login" + @@ -371,7 +379,7 @@ public final class IssueOrPullRequestQuery: GraphQLQuery { " ... on MergedEvent {" + " __typename" + " ...nodeFields" + - " commit {" + + " mergedCommit: commit {" + " __typename" + " oid" + " }" + @@ -792,6 +800,8 @@ public final class IssueOrPullRequestQuery: GraphQLQuery { public let actor: Actor? /// Identifies the date and time when the object was created. public let createdAt: String + /// Identifies the commit associated with the 'closed' event. + public let closedCommit: ClosedCommit? public let fragments: Fragments @@ -799,6 +809,7 @@ public final class IssueOrPullRequestQuery: GraphQLQuery { __typename = try reader.value(for: Field(responseName: "__typename")) actor = try reader.optionalValue(for: Field(responseName: "actor")) createdAt = try reader.value(for: Field(responseName: "createdAt")) + closedCommit = try reader.optionalValue(for: Field(responseName: "closedCommit", fieldName: "commit")) let nodeFields = try NodeFields(reader: reader) fragments = Fragments(nodeFields: nodeFields) @@ -818,6 +829,17 @@ public final class IssueOrPullRequestQuery: GraphQLQuery { login = try reader.value(for: Field(responseName: "login")) } } + + public struct ClosedCommit: GraphQLMappable { + public let __typename: String + /// The Git object ID + public let oid: String + + public init(reader: GraphQLResultReader) throws { + __typename = try reader.value(for: Field(responseName: "__typename")) + oid = try reader.value(for: Field(responseName: "oid")) + } + } } public struct AsReopenedEvent: GraphQLConditionalFragment { @@ -1504,6 +1526,8 @@ public final class IssueOrPullRequestQuery: GraphQLQuery { public let actor: Actor? /// Identifies the date and time when the object was created. public let createdAt: String + /// Identifies the commit associated with the 'closed' event. + public let closedCommit: ClosedCommit? public let fragments: Fragments @@ -1511,6 +1535,7 @@ public final class IssueOrPullRequestQuery: GraphQLQuery { __typename = try reader.value(for: Field(responseName: "__typename")) actor = try reader.optionalValue(for: Field(responseName: "actor")) createdAt = try reader.value(for: Field(responseName: "createdAt")) + closedCommit = try reader.optionalValue(for: Field(responseName: "closedCommit", fieldName: "commit")) let nodeFields = try NodeFields(reader: reader) fragments = Fragments(nodeFields: nodeFields) @@ -1530,6 +1555,17 @@ public final class IssueOrPullRequestQuery: GraphQLQuery { login = try reader.value(for: Field(responseName: "login")) } } + + public struct ClosedCommit: GraphQLMappable { + public let __typename: String + /// The Git object ID + public let oid: String + + public init(reader: GraphQLResultReader) throws { + __typename = try reader.value(for: Field(responseName: "__typename")) + oid = try reader.value(for: Field(responseName: "oid")) + } + } } public struct AsReopenedEvent: GraphQLConditionalFragment { @@ -1691,7 +1727,7 @@ public final class IssueOrPullRequestQuery: GraphQLQuery { /// Identifies the date and time when the object was created. public let createdAt: String /// Identifies the commit associated with the `merge` event. - public let commit: Commit + public let mergedCommit: MergedCommit public let fragments: Fragments @@ -1699,7 +1735,7 @@ public final class IssueOrPullRequestQuery: GraphQLQuery { __typename = try reader.value(for: Field(responseName: "__typename")) actor = try reader.optionalValue(for: Field(responseName: "actor")) createdAt = try reader.value(for: Field(responseName: "createdAt")) - commit = try reader.value(for: Field(responseName: "commit")) + mergedCommit = try reader.value(for: Field(responseName: "mergedCommit", fieldName: "commit")) let nodeFields = try NodeFields(reader: reader) fragments = Fragments(nodeFields: nodeFields) @@ -1720,7 +1756,7 @@ public final class IssueOrPullRequestQuery: GraphQLQuery { } } - public struct Commit: GraphQLMappable { + public struct MergedCommit: GraphQLMappable { public let __typename: String /// The Git object ID public let oid: String diff --git a/gql/IssueOrPullRequest.graphql b/gql/IssueOrPullRequest.graphql index ad4a8169..9a757bae 100644 --- a/gql/IssueOrPullRequest.graphql +++ b/gql/IssueOrPullRequest.graphql @@ -37,6 +37,7 @@ query IssueOrPullRequest($owner: String!, $repo: String!, $number: Int!, $page_s } ... on ClosedEvent { ...nodeFields + closedCommit: commit {oid} actor {login} createdAt } @@ -64,9 +65,7 @@ query IssueOrPullRequest($owner: String!, $repo: String!, $number: Int!, $page_s ... on ReferencedEvent { createdAt ...nodeFields - refCommit: commit { - oid - } + refCommit: commit {oid} actor {login} commitRepository { ...referencedRepositoryFields @@ -151,6 +150,7 @@ query IssueOrPullRequest($owner: String!, $repo: String!, $number: Int!, $page_s } ... on ClosedEvent { ...nodeFields + closedCommit: commit {oid} actor {login} createdAt } @@ -177,9 +177,7 @@ query IssueOrPullRequest($owner: String!, $repo: String!, $number: Int!, $page_s } ... on MergedEvent { ...nodeFields - commit { - oid - } + mergedCommit: commit {oid} actor {login} createdAt }