Skip to content
This repository has been archived by the owner on Sep 20, 2023. It is now read-only.

Templates #2400

Open
wants to merge 39 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
578ad4e
Added Templates
Huddie Nov 4, 2018
c46f387
Implemented review notes(1)
Huddie Nov 5, 2018
76d4ec2
Implement review notes(2)
Huddie Nov 5, 2018
ec7d437
Switched from master to default branch for template lookup
Huddie Nov 9, 2018
a175329
Created SpinnerTabelCell to allow spinner on styledTextCell
Huddie Nov 9, 2018
7568d57
Condensed createWithTemplate into create "constructor"
Huddie Nov 9, 2018
eff2648
Set signature type by repo owner (GitHawkApp) not template title
Huddie Nov 9, 2018
62c703b
Adds regular issue
Huddie Nov 10, 2018
6291a22
- Made string nil if no template is passed through
Huddie Nov 10, 2018
a22e144
- Cleaned template of details
Huddie Nov 11, 2018
8e9d191
Merge branch 'master' into templates
Huddie Nov 11, 2018
a08002a
Implement review notes
Huddie Nov 11, 2018
f9129aa
Implemented review comments
Huddie Nov 11, 2018
b6a3df1
Unlocalized storyboard name
Huddie Nov 11, 2018
a2c1a52
Fixed spelling
Huddie Nov 11, 2018
abaea27
Removed from constants
Huddie Nov 14, 2018
4714442
Fixed GitHub issue string
Huddie Nov 14, 2018
c083803
Started adding tests
Huddie Nov 15, 2018
3df004b
Fixing merge
Huddie Nov 18, 2018
957505b
Fixing merge
Huddie Nov 18, 2018
47bf411
Fixing merge conflict
Huddie Nov 18, 2018
f2c6563
Added a test
Huddie Nov 20, 2018
b058de2
Merge branch 'master' into templates
Huddie Nov 30, 2018
a95f7ca
Fix Merge
Huddie Nov 30, 2018
0ef9447
Small edit
Huddie Nov 30, 2018
a2c267f
Fixed tests
Huddie Nov 30, 2018
b381d40
Cleaned extra whitespace
Huddie Dec 1, 2018
4e075b3
Constants + alignment fixes
Huddie Dec 1, 2018
7f2d7b2
Removed passing around delegate + cleanup
Huddie Dec 1, 2018
7bb02a4
Renamed file + refactor
Huddie Dec 2, 2018
348c2ea
Small change in repo section
Huddie Dec 2, 2018
63d2e4c
Fixed iPad bug by setting popover source view
Huddie Dec 2, 2018
7381f21
More cleanup
Huddie Dec 2, 2018
c58be99
Enhanced regex + added test + cleaned up
Huddie Dec 2, 2018
4904268
Added back in nav controller
Huddie Dec 2, 2018
75f55b1
Small change
Huddie Dec 2, 2018
ee7b834
Small change
Huddie Dec 2, 2018
0dde120
Merge branch 'master' into templates
Huddie Dec 2, 2018
6321246
Merge branch 'master' into templates
Huddie Dec 11, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions Classes/New Issue/GithubClient+Template.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
//
// GithubClient+Template.swift
// Freetime
//
// Created by Ehud Adler on 11/11/18.
// Copyright © 2018 Ryan Nystrom. All rights reserved.
//

import Foundation
import GitHubAPI
import GitHubSession
import Squawk

private let githubIssueURL = ".github/ISSUE_TEMPLATE"


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra space

struct IssueTemplateRepoDetails {
let repo: RepositoryDetails
let defaultBranch: String
}

struct IssueTemplateDetails {
let repoDetails: IssueTemplateRepoDetails
let session: GitHubUserSession?
let viewController: UIViewController
weak var delegate: NewIssueTableViewControllerDelegate?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we passing around a delegate like this? It's coupling way too many things. Networking should just do networking, parsing should just do parsing.

Instead we have a networker that is also showing alerts and presenting VCs. Too much! That work should be done by the calling VC in the completion block. If there's duplicated code, just create a func that both VCs can call.

Also could we just fetch the list of templates, show the alert (if there are >0 templates) w/ file names or something w/out fetching the actual contents? Then when the user selects a template fetch the contents. There's just wasted networking in here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rnystrom We can't reduce the networking calls because the title and description of files are at the top of the files. The file names are not equal to the template names. Let me know if that makes sense. I will make the changes regarding passing the templates to the VC and letting it take care of displaying the alert.

}

extension GithubClient {

private func fetchTemplateFile(
repoDetails: IssueTemplateRepoDetails,
filename: String,
testingFile: String? = nil,
completion: @escaping (Result<String>) -> Void
) {

// For Testing..
if let testingFile = testingFile { completion(.success(testingFile)) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's newline the closure execution here to stay consistent w/ other ifs


self.fetchFile(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove self

owner: repoDetails.repo.owner,
repo: repoDetails.repo.name,
branch: repoDetails.defaultBranch,
path: "\(githubIssueURL)/\(filename)") { (result) in
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove parens around result

switch result {
case .success(let file): completion(.success(file))
case .error(let error): completion(.error(error))
case .nonUTF8: completion(.error(nil))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: spacing inconsistent w/ rest of project. Newlines or single-space please

}
}
}

func createTemplate(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this needs a new name, I keep thinking that it's actually creating something. Can it just use the "fetch" naming used elsewhere?

repo: IssueTemplateRepoDetails,
filename: String,
testingFile: String? = nil,
completion: @escaping (Result<IssueTemplate>) -> Void
) {
fetchTemplateFile(repoDetails: repo, filename: filename, testingFile: testingFile) { (result) in
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove parens around (result)

switch result {
case .success(let file):
let nameAndDescription = IssueTemplateHelper.getNameAndDescription(fromTemplatefile: file)
if let name = nameAndDescription.name {
var cleanedFile = file

// Remove all template detail text
// -----
// name:
// about:
// -----
if let textToClean = file.matches(regex: "([-]{3,})([\\s\\S]*)([-]{3,})").first {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add individual tests for this?

if let range = file.range(of: textToClean) {
cleanedFile = file.replacingOccurrences(
of: textToClean,
with: "",
options: .literal,
range: range
)
}
cleanedFile = cleanedFile.trimmingCharacters(in: .whitespacesAndNewlines)
}
completion(.success(IssueTemplate(title: name, template: cleanedFile)))
} else {
completion(.error(nil))
}
case .error(let error):
completion(.error(error))
}
}
}

func createNewIssue(
repo: IssueTemplateRepoDetails,
session: GitHubUserSession?,
mainViewController: UIViewController,
delegate: NewIssueTableViewControllerDelegate,
completion: @escaping () -> Void
) {

var templates: [IssueTemplate] = []

// Create group.
// We need this since we will be making multiple async calls inside a for-loop.
let templateGroup = DispatchGroup()

let details = IssueTemplateDetails(
repoDetails: repo,
session: session,
viewController: mainViewController,
delegate: delegate
)

self.fetchFiles(
owner: details.repoDetails.repo.owner,
repo: details.repoDetails.repo.name,
branch: details.repoDetails.defaultBranch,
path: githubIssueURL) { (result) in
switch result {
case .success(let files):
for file in files {
templateGroup.enter()
self.createTemplate(repo: repo, filename: file.name, completion: { (result) in
switch result {
case .success(let template):
templates.append(template)
case .error(let error):
if let err = error { Squawk.show(error: err) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VC should be showing error alerts. Also what happens if multiple things go wrong, we're showing multiple errors? Should just bubble up to a single error and let the VC handle it.

}
templateGroup.leave()
})
}
case .error(let error):
if let err = error { Squawk.show(error: err) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

completion()
}

// Wait for async calls in for-loop to finish up
templateGroup.notify(queue: .main) {
completion()
// Sort lexicographically
let sortedTemplates = templates.sorted(by: {$0.title < $1.title })
IssueTemplateHelper.present(
withTemplates: sortedTemplates,
details: details
)
}
}
}
}
108 changes: 108 additions & 0 deletions Classes/New Issue/IssueTemplateHelper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//
// IssueTemplates.swift
// Freetime
//
// Created by Ehud Adler on 11/3/18.
// Copyright © 2018 Ryan Nystrom. All rights reserved.
//

import Foundation
import GitHubAPI
import GitHubSession
import Squawk

struct IssueTemplate {
let title: String
let template: String
}

class IssueTemplateHelper {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final


static func getNameAndDescription(fromTemplatefile file: String) -> (name: String?, about: String?) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

glad this is tested 😄

let names = file.matches(regex: String.getRegexForLine(after: "name"))
let abouts = file.matches(regex: String.getRegexForLine(after: "about"))
let name = names.first?.trimmingCharacters(in: .whitespaces)
let about = abouts.first?.trimmingCharacters(in: .whitespaces)
return (name, about)
}

static func showIssueAlert(
with templates: [IssueTemplate],
details: IssueTemplateDetails) -> UIAlertController {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curly paren on newline



Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too much whitespace

let alertView = UIAlertController(
title: NSLocalizedString("New Issue", comment: ""),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constants

message: NSLocalizedString("Choose Template", comment: ""),
preferredStyle: .actionSheet
)

for template in templates {
alertView.addAction(
UIAlertAction(
title: template.title,
style: .default,
handler: { _ in
guard let viewController = NewIssueTableViewController.create(
client: GithubClient(userSession: details.session),
owner: details.repoDetails.repo.owner,
repo: details.repoDetails.repo.name,
template: template.template,
signature: details.repoDetails.repo.owner == Constants.GitHawk.owner ? .bugReport : .sentWithGitHawk
) else {
assertionFailure("Failed to create NewIssueTableViewController")
return
}
viewController.delegate = details.delegate
let navController = UINavigationController(rootViewController: viewController)
navController.modalPresentationStyle = .formSheet
details.viewController.route_present(to: navController)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

router handles wrapping in nav controller

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rnystrom Can you explain this a bit? If I switch to route_present(to: viewController) no navigation bar shows in the pushed view. I think I may not fully understand.

Copy link
Collaborator Author

@Huddie Huddie Dec 2, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also a separate question: Should it be route_detail? on iPad it pushes the new VC over the entire screen, not into the detail section

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see now that route_details wraps a nav controller if necessary. Cool stuff. Ill make this switch.

}))
}

alertView.addAction(
UIAlertAction(
title: NSLocalizedString("Dismiss", comment: ""),
style: .cancel,
handler: { _ in
alertView.dismiss(animated: trueUnlessReduceMotionEnabled)
})
)
return alertView
}

static func present(
withTemplates sortedTemplates: [IssueTemplate],
details: IssueTemplateDetails) {
if sortedTemplates.count > 0 {
// Templates exists...
var templates = sortedTemplates
templates.append(
IssueTemplate(
title: NSLocalizedString("Regular Issue", comment: ""),
template: ""
)
)
let alertView = IssueTemplateHelper.showIssueAlert(
with: templates,
details: details
)
details.viewController.route_present(to: alertView)
} else {
// No templates exists, show blank new issue view controller
guard let viewController = NewIssueTableViewController.create(
client: GithubClient(userSession: details.session),
owner: details.repoDetails.repo.owner,
repo: details.repoDetails.repo.name,
signature: .sentWithGitHawk
) else {
assertionFailure("Failed to create NewIssueTableViewController")
return
}
viewController.delegate = details.delegate
let navController = UINavigationController(rootViewController: viewController)
navController.modalPresentationStyle = .formSheet
details.viewController.route_present(to: navController)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same note about nav controller, router handles wrapping

}
}
}
15 changes: 11 additions & 4 deletions Classes/New Issue/NewIssueTableViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ final class NewIssueTableViewController: UITableViewController, UITextFieldDeleg
@IBOutlet var titleField: UITextField!
@IBOutlet var bodyField: UITextView!

private var template: String?
private var client: GithubClient!
private var owner: String!
private var repo: String!
Expand All @@ -69,17 +70,22 @@ final class NewIssueTableViewController: UITableViewController, UITextFieldDeleg
return raw
}

static func create(client: GithubClient,
owner: String,
repo: String,
signature: IssueSignatureType? = nil) -> NewIssueTableViewController? {
static func create(
client: GithubClient,
owner: String,
repo: String,
template: String? = nil,
signature: IssueSignatureType? = nil
) -> NewIssueTableViewController? {

let storyboard = UIStoryboard(name: "NewIssue", bundle: nil)

let viewController = storyboard.instantiateInitialViewController() as? NewIssueTableViewController
Huddie marked this conversation as resolved.
Show resolved Hide resolved
viewController?.hidesBottomBarWhenPushed = true
viewController?.client = client
viewController?.owner = owner
viewController?.repo = repo
viewController?.template = template
viewController?.signature = signature

return viewController
Expand All @@ -106,6 +112,7 @@ final class NewIssueTableViewController: UITableViewController, UITextFieldDeleg
// Setup markdown input view
bodyField.githawkConfigure(inset: false)
setupInputView()
bodyField.text = template

// Update title to use localization
title = Constants.Strings.newIssue
Expand Down
30 changes: 16 additions & 14 deletions Classes/Repository/RepositoryViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -235,22 +235,24 @@ EmptyViewDelegate {
}

func newIssueAction() -> UIAlertAction? {
guard case .value(let details) = state,
details.hasIssuesEnabled,
let newIssueViewController = NewIssueTableViewController.create(
client: client,
owner: repo.owner,
repo: repo.name,
signature: .sentWithGitHawk)
else {
return nil
}

newIssueViewController.delegate = self
weak var weakSelf = self
guard case .value(let details) = self.state else { return nil }

let repoDetails = IssueTemplateRepoDetails(
repo: repo,
defaultBranch: details.defaultBranch
)

return AlertAction(AlertActionBuilder { $0.rootViewController = weakSelf })
.newIssue(issueController: newIssueViewController)
let action = UIAlertAction(title: NSLocalizedString("New Issue", comment: ""), style: .default) { _ in
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a Constants.String for this

self.client.createNewIssue(
repo: repoDetails,
session: nil,
mainViewController: self,
delegate: self,
completion: {
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty block?

}
return action
}

func workingCopyAction() -> UIAlertAction? {
Expand Down
4 changes: 2 additions & 2 deletions Classes/Settings/Settings.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,14 @@
</tableViewSection>
<tableViewSection headerTitle="🦅 Githawk" id="3QH-oa-l2C">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="kdx-s1-NXl" style="IBUITableViewCellStyleDefault" id="WwO-m1-s3q" customClass="StyledTableCell" customModule="Freetime" customModuleProvider="target">
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="kdx-s1-NXl" style="IBUITableViewCellStyleDefault" id="WwO-m1-s3q" customClass="SpinnerTableCell" customModule="Freetime" customModuleProvider="target">
Huddie marked this conversation as resolved.
Show resolved Hide resolved
<rect key="frame" x="0.0" y="243.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="WwO-m1-s3q" id="DTP-jg-lt7">
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Report a Bug" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="kdx-s1-NXl" customClass="SettingsLabel" customModule="Freetime" customModuleProvider="target">
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Give Feedback" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="kdx-s1-NXl" customClass="SettingsLabel" customModule="Freetime" customModuleProvider="target">
<rect key="frame" x="16" y="0.0" width="324" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
Expand Down
Loading