Files
GitHawk/Classes/PullRequestReviews/GithubClient+PullRequestReviewComments.swift

218 lines
7.2 KiB
Swift

//
// GithubClient+PullRequestReviewComments.swift
// Freetime
//
// Created by Ryan Nystrom on 11/4/17.
// Copyright © 2017 Ryan Nystrom. All rights reserved.
//
import UIKit
import IGListKit
import GitHubAPI
import StyledTextKit
import Squawk
extension GithubClient {
fileprivate struct ReviewComment {
let id: Int
let body: String
let created: Date
let author: String
let authorAvatarURL: URL
}
func fetchPRComments(
owner: String,
repo: String,
number: Int,
width: CGFloat,
completion: @escaping (Result<[ListDiffable]>) -> ()
) {
let viewerUsername = userSession?.username
let contentSizeCategory = UIContentSizeCategory.preferred
client.send(V3PullRequestCommentsRequest(owner: owner, repo: repo, number: number)) { result in
switch result {
case .failure(let error):
Squawk.showGenericError()
completion(.error(error))
case .success(let response):
struct Thread {
let hunk: String
let path: String
var comments = [ReviewComment]()
}
DispatchQueue.global().async {
var threadIDs = [Int]()
var threads = [Int: Thread]()
for json in response.data {
guard let id = json["id"] as? Int,
let reviewComment = createReviewCommentModel(id: id, json: json)
else { continue }
if let replyID = json["in_reply_to_id"] as? Int {
// this should exist since the root will always be posted before the reply
// payload is delivered sorted by creation date descending
threads[replyID]?.comments.append(reviewComment)
} else {
guard let diffHunk = json["diff_hunk"] as? String,
let path = json["path"] as? String
else { continue }
threads[id] = Thread(
hunk: diffHunk,
path: path,
comments: [reviewComment]
)
// create diff hunk and root comment
// append to threads and threadIDs
threadIDs.append(id)
}
}
var models = [ListDiffable]()
for id in threadIDs {
guard let thread = threads[id] else { continue }
let code = CreateDiffString(code: thread.hunk, limit: true)
let text = StyledTextRenderer(
string: code,
contentSizeCategory: contentSizeCategory,
inset: IssueDiffHunkPreviewCell.textViewInset
)
models.append(IssueDiffHunkModel(path: thread.path, preview: text, offset: models.count))
for comment in thread.comments {
models.append(createReviewComment(
owner: owner,
repo: repo,
model: comment,
viewer: viewerUsername,
contentSizeCategory: contentSizeCategory,
width: width
))
}
models.append(PullRequestReviewReplyModel(replyID: id))
}
DispatchQueue.main.async {
completion(.success(models))
}
}
}
}
}
func sendComment(
body: String,
inReplyTo: Int,
owner: String,
repo: String,
number: Int,
width: CGFloat,
completion: @escaping (Result<IssueCommentModel>) -> ()
) {
let viewer = userSession?.username
let contentSizeCategory = UIContentSizeCategory.preferred
client.send(V3SendPullRequestCommentRequest(
owner: owner,
repo: repo,
number: number,
body: body,
inReplyTo: inReplyTo)
) { result in
switch result {
case .success(let response):
if let model = createReviewCommentModel(id: response.id, json: response.data) {
let comment = createReviewComment(
owner: owner,
repo: repo,
model: model,
viewer: viewer,
contentSizeCategory: contentSizeCategory,
width: width
)
completion(.success(comment))
} else {
Squawk.showGenericError()
completion(.error(nil))
}
case .failure(let error):
Squawk.showGenericError()
completion(.error(error))
}
}
}
}
private func createReviewCommentModel(id: Int, json: [String: Any]) -> GithubClient.ReviewComment? {
guard let userJSON = json["user"] as? [String: Any],
let userAvatar = userJSON["avatar_url"] as? String,
let userAvatarURL = URL(string: userAvatar),
let createdString = json["created_at"] as? String,
let created = createdString.githubDate,
let login = userJSON["login"] as? String
else { return nil }
return GithubClient.ReviewComment(
id: id,
body: json["body"] as? String ?? "",
created: created,
author: login,
authorAvatarURL: userAvatarURL
)
}
private func createReviewComment(
owner: String,
repo: String,
model: GithubClient.ReviewComment,
viewer: String?,
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,
sentWithGitHawk: checkedMarkdown.sentWithGitHawk
)
let reactions = IssueCommentReactionViewModel(models: [])
let bodies = MarkdownModels(
checkedMarkdown.markdown,
owner: owner,
repo: repo,
width: width,
viewerCanUpdate: false,
contentSizeCategory: contentSizeCategory,
isRoot: false
)
return IssueCommentModel(
id: "\(model.id)",
details: details,
bodyModels: bodies,
reactions: reactions,
collapse: nil,
threadState: .neck,
rawMarkdown: model.body,
viewerCanUpdate: false,
viewerCanDelete: false,
isRoot: false,
number: model.id,
asReviewComment: true
)
}