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

Commit 7c0360a

Browse files
committed
Adds edit issue title
Use localized string for title
1 parent 7ab377d commit 7c0360a

File tree

8 files changed

+919
-609
lines changed

8 files changed

+919
-609
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//
2+
// EditIssueTitleViewController.swift
3+
// Freetime
4+
//
5+
// Created by B_Litwin on 10/22/18.
6+
// Copyright © 2018 Ryan Nystrom. All rights reserved.
7+
//
8+
9+
import UIKit
10+
import GitHubAPI
11+
import SnapKit
12+
13+
protocol EditIssueTitleViewControllerDelegate: class {
14+
func sendEditTitleRequest(newTitle: String, viewController: EditIssueTitleViewController)
15+
var currentIssueTitle: String? { get }
16+
var viewerCanUpdate: Bool { get }
17+
}
18+
19+
class EditIssueTitleViewController: UIViewController {
20+
21+
private let textView = UITextView()
22+
private let issueTitle: String
23+
private weak var delegate: EditIssueTitleViewControllerDelegate?
24+
25+
init(delegate: EditIssueTitleViewControllerDelegate) {
26+
self.delegate = delegate
27+
self.issueTitle = delegate.currentIssueTitle ?? ""
28+
super.init(nibName: nil, bundle: nil)
29+
}
30+
31+
required init?(coder aDecoder: NSCoder) {
32+
fatalError("init(coder:) has not been implemented")
33+
}
34+
35+
override func viewDidLoad() {
36+
super.viewDidLoad()
37+
preferredContentSize = CGSize(
38+
width: Styles.Sizes.contextMenuSize.width,
39+
height: 120
40+
)
41+
title = NSLocalizedString("Edit", comment: "")
42+
43+
view.addSubview(textView)
44+
textView.snp.makeConstraints { make in
45+
make.edges.equalTo(view)
46+
}
47+
textView.textContainerInset = Styles.Sizes.textViewInset
48+
textView.text = issueTitle
49+
50+
setRightBarItemIdle()
51+
navigationItem.leftBarButtonItem = UIBarButtonItem(
52+
title: Constants.Strings.cancel,
53+
style: .plain,
54+
target: self,
55+
action: #selector(
56+
EditIssueTitleViewController.onMenuCancel
57+
)
58+
)
59+
}
60+
61+
override func viewDidAppear(_ animated: Bool) {
62+
super.viewDidAppear(animated)
63+
textView.becomeFirstResponder()
64+
}
65+
66+
func setRightBarItemIdle() {
67+
navigationItem.rightBarButtonItem = UIBarButtonItem(
68+
title: NSLocalizedString("Save", comment: ""),
69+
style: .plain,
70+
target: self,
71+
action: #selector(
72+
EditIssueTitleViewController.onMenuSave
73+
)
74+
)
75+
}
76+
77+
@objc func onMenuSave() {
78+
textView.isEditable = false
79+
textView.resignFirstResponder()
80+
guard textView.text != issueTitle else { return }
81+
setRightBarItemSpinning()
82+
delegate?.sendEditTitleRequest(
83+
newTitle: textView.text,
84+
viewController: self
85+
)
86+
}
87+
88+
@objc func onMenuCancel() {
89+
textView.resignFirstResponder()
90+
dismiss(animated: true)
91+
}
92+
93+
}

Classes/Issues/IssueManagingContextController.swift

+28
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ final class IssueManagingContextController: NSObject, ContextMenuDelegate {
4747
}
4848
let client: GithubClient
4949
weak var viewController: UIViewController?
50+
weak var editIssueTitleViewControllerDelegate: EditIssueTitleViewControllerDelegate?
5051

5152
init(model: IssueDetailsModel, client: GithubClient) {
5253
let button = IssueManageButton()
@@ -78,6 +79,7 @@ final class IssueManagingContextController: NSObject, ContextMenuDelegate {
7879
case lock
7980
case reopen
8081
case close
82+
case editTitle
8183
}
8284

8385
var actions: [Action] {
@@ -91,6 +93,16 @@ final class IssueManagingContextController: NSObject, ContextMenuDelegate {
9193
if result.pullRequest {
9294
actions.append(.reviewers)
9395
}
96+
97+
let viewerCanUpdate =
98+
editIssueTitleViewControllerDelegate?
99+
.viewerCanUpdate
100+
?? false
101+
102+
if viewerCanUpdate {
103+
actions.append(.editTitle)
104+
}
105+
94106
if result.labels.locked {
95107
actions.append(.unlock)
96108
} else {
@@ -138,6 +150,9 @@ final class IssueManagingContextController: NSObject, ContextMenuDelegate {
138150
case .close:
139151
title = Constants.Strings.close
140152
iconName = "x"
153+
case .editTitle:
154+
title = NSLocalizedString("Edit Title", comment: "")
155+
iconName = "pencil"
141156
}
142157

143158
// Lock always has the divider above it assuming you're a collaborator.
@@ -180,6 +195,7 @@ final class IssueManagingContextController: NSObject, ContextMenuDelegate {
180195
case .lock: strongSelf.lock(true)
181196
case .reopen: strongSelf.close(false)
182197
case .close: strongSelf.close(true)
198+
case .editTitle: strongSelf.presentEditTitleController()
183199
}
184200
}
185201
}
@@ -264,6 +280,18 @@ final class IssueManagingContextController: NSObject, ContextMenuDelegate {
264280
delegate: self
265281
)
266282
}
283+
284+
func presentEditTitleController() {
285+
guard let viewController = viewController else { return }
286+
guard let delegate = editIssueTitleViewControllerDelegate else { return }
287+
let controller = EditIssueTitleViewController(
288+
delegate: delegate
289+
)
290+
ContextMenu.shared.show(
291+
sourceViewController: viewController,
292+
viewController: controller
293+
)
294+
}
267295

268296
func close(_ doClose: Bool) {
269297
guard let previous = result else { return }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//
2+
// IssuesViewController+UpdateTitle.swift
3+
// Freetime
4+
//
5+
// Created by B_Litwin on 10/23/18.
6+
// Copyright © 2018 Ryan Nystrom. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
extension IssuesViewController {
12+
13+
static func updateBookmarkTitle(
14+
newTitle: String,
15+
bookmark: Bookmark,
16+
bookmarkStore: BookmarkStore
17+
)
18+
{
19+
20+
let newBookmark = Bookmark(
21+
type: bookmark.type,
22+
name: bookmark.name,
23+
owner: bookmark.owner,
24+
number: bookmark.number,
25+
title: newTitle,
26+
defaultBranch: bookmark.defaultBranch
27+
)
28+
29+
if let index = bookmarkStore.values.index(of: bookmark) {
30+
bookmarkStore.values[index] = newBookmark
31+
bookmarkStore.save()
32+
}
33+
}
34+
35+
static func updateIssueResultModelTitle(
36+
newTitle: String,
37+
oldTitle: String,
38+
username: String,
39+
issueResultModel: IssueResult,
40+
width: CGFloat
41+
) -> IssueResult
42+
{
43+
44+
let title = titleStringSizing(
45+
title: newTitle,
46+
contentSizeCategory: UIContentSizeCategory.preferred,
47+
width: width
48+
)
49+
50+
let titleChangeString = IssueRenamedString(
51+
previous: oldTitle,
52+
current: newTitle,
53+
contentSizeCategory: UIContentSizeCategory.preferred,
54+
width: width
55+
)
56+
57+
let issueRenamedModel = IssueRenamedModel(
58+
id: UUID().uuidString,
59+
actor: username,
60+
date: Date(),
61+
titleChangeString: titleChangeString
62+
)
63+
64+
let issueResult = issueResultModel.updated(
65+
title: title,
66+
timelinePages: issueResultModel.timelinePages(appending: [issueRenamedModel])
67+
)
68+
69+
return issueResult
70+
}
71+
}

Classes/Issues/IssuesViewController.swift

+66-6
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ final class IssuesViewController: MessageViewController,
3535
IssueTextActionsViewSendDelegate,
3636
EmptyViewDelegate,
3737
MessageTextViewListener,
38-
IssueLabelTapSectionControllerDelegate
38+
IssueLabelTapSectionControllerDelegate,
39+
EditIssueTitleViewControllerDelegate
3940
{
4041

4142
private let client: GithubClient
@@ -135,6 +136,7 @@ final class IssuesViewController: MessageViewController,
135136
cacheKey = "issue.\(model.owner).\(model.repo).\(model.number)"
136137

137138
manageController.viewController = self
139+
manageController.editIssueTitleViewControllerDelegate = self
138140
}
139141

140142
required init?(coder aDecoder: NSCoder) {
@@ -286,6 +288,13 @@ final class IssuesViewController: MessageViewController,
286288
activityController.popoverPresentationController?.barButtonItem = sender
287289
present(activityController, animated: trueUnlessReduceMotionEnabled)
288290
}
291+
292+
var insetWidth: CGFloat {
293+
// assumptions here, but the collectionview may not have been laid out or content size found
294+
// assume the collectionview is pinned to the view's bounds
295+
let contentInset = feed.collectionView.contentInset
296+
return view.bounds.width - contentInset.left - contentInset.right
297+
}
289298

290299
func fetch(previous: Bool) {
291300
if !previous {
@@ -312,10 +321,7 @@ final class IssuesViewController: MessageViewController,
312321
}
313322
}
314323

315-
// assumptions here, but the collectionview may not have been laid out or content size found
316-
// assume the collectionview is pinned to the view's bounds
317-
let contentInset = feed.collectionView.contentInset
318-
let width = view.bounds.width - contentInset.left - contentInset.right
324+
let width = insetWidth
319325

320326
client.fetch(
321327
owner: model.owner,
@@ -405,7 +411,7 @@ final class IssuesViewController: MessageViewController,
405411
metadata.append(IssueFileChangesModel(changes: changes))
406412
}
407413
// END metadata collection
408-
414+
409415
objects.append(IssueTitleModel(string: current.title))
410416
objects += metadata
411417

@@ -642,5 +648,59 @@ final class IssuesViewController: MessageViewController,
642648
guard let issueType = self.issueType else { return }
643649
presentLabels(client: client, owner: owner, repo: repo, label: label, type: issueType)
644650
}
651+
652+
// MARK: EditIssueTitleViewControllerDelegate
645653

654+
func sendEditTitleRequest(newTitle: String, viewController: EditIssueTitleViewController) {
655+
let request = V3EditIssueTitleRequest(
656+
owner: model.owner,
657+
repo: model.repo,
658+
issueNumber: model.number,
659+
title: newTitle
660+
)
661+
662+
client.client.send(request) { [weak self] result in
663+
switch result {
664+
case .success:
665+
guard let strongSelf = self else { return }
666+
667+
// Update Bookmark with new title
668+
if let bookmark = strongSelf.bookmark,
669+
let bookmarkStore = strongSelf.client.bookmarksStore {
670+
IssuesViewController.updateBookmarkTitle(
671+
newTitle: newTitle,
672+
bookmark: bookmark,
673+
bookmarkStore: bookmarkStore
674+
)
675+
}
676+
677+
// Update issueResult model and timeline
678+
if let current = strongSelf.result {
679+
let issueResult = IssuesViewController.updateIssueResultModelTitle(
680+
newTitle: newTitle,
681+
oldTitle: strongSelf.currentIssueTitle ?? "",
682+
username: strongSelf.client.userSession?.username ?? Constants.Strings.unknown,
683+
issueResultModel: current,
684+
width: strongSelf.insetWidth
685+
)
686+
strongSelf.client.cache.set(value: issueResult)
687+
}
688+
689+
case .failure(let error):
690+
Squawk.show(error: error)
691+
}
692+
693+
viewController.setRightBarItemIdle()
694+
viewController.dismiss(animated: true)
695+
}
696+
}
697+
698+
var currentIssueTitle: String? {
699+
return result?.title.string.allText
700+
}
701+
702+
var viewerCanUpdate: Bool {
703+
return result?.viewerCanUpdate ?? false
704+
}
705+
646706
}

0 commit comments

Comments
 (0)