Skip to content

Commit 68a8b80

Browse files
authored
Merge pull request #17911 from mertGokduman/issue/17528-17534-Page-Post-List-Copy-link-functionality
Issue/17528-17534 - Page and Post list copy link functionality
2 parents 5713706 + d615641 commit 68a8b80

8 files changed

Lines changed: 76 additions & 14 deletions

WordPress/Classes/ViewRelated/Pages/PageListViewController.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Foundation
22
import CocoaLumberjack
33
import WordPressShared
44
import WordPressFlux
5+
import UIKit
56

67
class PageListViewController: AbstractPostListViewController, UIViewControllerRestoration {
78
private struct Constant {
@@ -542,6 +543,15 @@ class PageListViewController: AbstractPostListViewController, UIViewControllerRe
542543
present(editorViewController, animated: false)
543544
}
544545

546+
fileprivate func copyLink(_ page: Page) {
547+
let pasteboard = UIPasteboard.general
548+
guard let link = page.permaLink else { return }
549+
pasteboard.string = link as String
550+
let noticeTitle = NSLocalizedString("Link Copied to Clipboard", comment: "Link copied to clipboard notice title")
551+
let notice = Notice(title: noticeTitle, feedbackType: .success)
552+
ActionDispatcher.global.dispatch(NoticeAction.post(notice))
553+
}
554+
545555
fileprivate func retryPage(_ apost: AbstractPost) {
546556
PostCoordinator.shared.save(apost)
547557
}
@@ -680,6 +690,8 @@ class PageListViewController: AbstractPostListViewController, UIViewControllerRe
680690
}
681691
}
682692

693+
addCopyLinkAction(to: alertController, for: page)
694+
683695
if !isHomepage {
684696
alertController.addActionWithTitle(trashButtonTitle, style: .destructive, handler: { [weak self] (action) in
685697
guard let strongSelf = self,
@@ -725,6 +737,8 @@ class PageListViewController: AbstractPostListViewController, UIViewControllerRe
725737
})
726738
}
727739

740+
addCopyLinkAction(to: alertController, for: page)
741+
728742
alertController.addActionWithTitle(trashButtonTitle, style: .destructive, handler: { [weak self] (action) in
729743
guard let strongSelf = self,
730744
let page = strongSelf.pageForObjectID(objectID) else {
@@ -791,6 +805,15 @@ class PageListViewController: AbstractPostListViewController, UIViewControllerRe
791805
})
792806
}
793807

808+
private func addCopyLinkAction(to controller: UIAlertController, for page: AbstractPost) {
809+
let buttonTitle = NSLocalizedString("Copy Link", comment: "Label for page copy link. Tapping copy the url of page")
810+
controller.addActionWithTitle(buttonTitle, style: .default) { [weak self] _ in
811+
if let page = self?.pageForObjectID(page.objectID) {
812+
self?.copyLink(page)
813+
}
814+
}
815+
}
816+
794817
private func addSetParentAction(to controller: UIAlertController, for page: AbstractPost, at index: IndexPath?) {
795818
/// This button is disabled for trashed pages
796819
//

WordPress/Classes/ViewRelated/Post/AbstractPostListViewController.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Gridicons
33
import CocoaLumberjack
44
import WordPressShared
55
import wpxmlrpc
6+
import WordPressFlux
67

78
// FIXME: comparison operators with optionals were removed from the Swift Standard Libary.
89
// Consider refactoring the code to use the non-optional operators.
@@ -1061,6 +1062,15 @@ class AbstractPostListViewController: UIViewController,
10611062
}
10621063
}
10631064

1065+
@objc func copyPostLink(_ apost: AbstractPost) {
1066+
let pasteboard = UIPasteboard.general
1067+
guard let link = apost.permaLink else { return }
1068+
pasteboard.string = link as String
1069+
let noticeTitle = NSLocalizedString("Link Copied to Clipboard", comment: "Link copied to clipboard notice title")
1070+
let notice = Notice(title: noticeTitle, feedbackType: .success)
1071+
ActionDispatcher.global.dispatch(NoticeAction.post(notice))
1072+
}
1073+
10641074
@objc func promptThatPostRestoredToFilter(_ filter: PostListFilter) {
10651075
assert(false, "You should implement this method in the subclass")
10661076
}

WordPress/Classes/ViewRelated/Post/InteractivePostViewDelegate.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ import Foundation
1212
func retry(_ post: AbstractPost)
1313
func cancelAutoUpload(_ post: AbstractPost)
1414
func share(_ post: AbstractPost, fromView view: UIView)
15+
func copyLink(_ post: AbstractPost)
1516
}

WordPress/Classes/ViewRelated/Post/PostActionSheet.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ class PostActionSheet {
7878
}
7979
case .more:
8080
WordPressAppDelegate.crashLogging?.logMessage("Cannot handle unexpected button for post action sheet: \(button). This is a configuration error.", level: .error)
81+
case .copyLink:
82+
actionSheetController.addDefaultActionWithTitle(Titles.copyLink) { [weak self] _ in
83+
self?.interactivePostViewDelegate?.copyLink(post)
84+
}
8185
}
8286
}
8387

@@ -103,5 +107,6 @@ class PostActionSheet {
103107
static let retry = NSLocalizedString("Retry", comment: "Retry uploading the post.")
104108
static let edit = NSLocalizedString("Edit", comment: "Edit the post.")
105109
static let share = NSLocalizedString("Share", comment: "Share the post.")
110+
static let copyLink = NSLocalizedString("Copy Link", comment: "Copy the post url and paste anywhere in phone")
106111
}
107112
}

WordPress/Classes/ViewRelated/Post/PostCardStatusViewModel.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class PostCardStatusViewModel: NSObject {
1818
case trash
1919
case cancelAutoUpload
2020
case share
21+
case copyLink
2122
}
2223

2324
struct ButtonGroups: Equatable {
@@ -190,6 +191,10 @@ class PostCardStatusViewModel: NSObject {
190191
buttons.append(.moveToDraft)
191192
}
192193

194+
if post.status != .trash {
195+
buttons.append(.copyLink)
196+
}
197+
193198
buttons.append(.trash)
194199

195200
return buttons

WordPress/Classes/ViewRelated/Post/PostListViewController.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,10 @@ class PostListViewController: AbstractPostListViewController, UIViewControllerRe
676676
}
677677
}
678678

679+
func copyLink(_ post: AbstractPost) {
680+
copyPostLink(post)
681+
}
682+
679683
func trash(_ post: AbstractPost) {
680684
guard ReachabilityUtils.isInternetReachable() else {
681685
let offlineMessage = NSLocalizedString("Unable to trash posts while offline. Please try again later.", comment: "Message that appears when a user tries to trash a post while their device is offline.")

WordPress/WordPressTest/PostActionSheetTests.swift

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class PostActionSheetTests: XCTestCase {
2525
postActionSheet.show(for: viewModel, from: view)
2626

2727
let options = viewControllerMock.viewControllerPresented?.actions.compactMap { $0.title }
28-
XCTAssertEqual(["Cancel", "Stats", "Share", "Duplicate", "Move to Draft", "Move to Trash"], options)
28+
XCTAssertEqual(["Cancel", "Stats", "Share", "Duplicate", "Move to Draft", "Copy Link", "Move to Trash"], options)
2929
}
3030

3131
func testLocallyPublishedPostShowsCancelAutoUploadOption() {
@@ -34,7 +34,7 @@ class PostActionSheetTests: XCTestCase {
3434
postActionSheet.show(for: viewModel, from: view, isCompactOrSearching: true)
3535

3636
let options = viewControllerMock.viewControllerPresented?.actions.compactMap { $0.title }
37-
XCTAssertEqual([Titles.cancel, Titles.cancelAutoUpload, Titles.duplicate, Titles.draft, Titles.trash], options)
37+
XCTAssertEqual([Titles.cancel, Titles.cancelAutoUpload, Titles.duplicate, Titles.draft, Titles.copyLink, Titles.trash], options)
3838
}
3939

4040
func testDraftedPostOptions() {
@@ -43,7 +43,7 @@ class PostActionSheetTests: XCTestCase {
4343
postActionSheet.show(for: viewModel, from: view)
4444

4545
let options = viewControllerMock.viewControllerPresented?.actions.compactMap { $0.title }
46-
XCTAssertEqual(["Cancel", "Publish Now", "Duplicate", "Move to Trash"], options)
46+
XCTAssertEqual(["Cancel", "Publish Now", "Duplicate", "Copy Link", "Move to Trash"], options)
4747
}
4848

4949
func testScheduledPostOptions() {
@@ -52,7 +52,7 @@ class PostActionSheetTests: XCTestCase {
5252
postActionSheet.show(for: viewModel, from: view)
5353

5454
let options = viewControllerMock.viewControllerPresented?.actions.compactMap { $0.title }
55-
XCTAssertEqual(["Cancel", "Move to Draft", "Move to Trash"], options)
55+
XCTAssertEqual(["Cancel", "Move to Draft", "Copy Link", "Move to Trash"], options)
5656
}
5757

5858
func testTrashedPostOptions() {
@@ -70,7 +70,7 @@ class PostActionSheetTests: XCTestCase {
7070
postActionSheet.show(for: viewModel, from: view, isCompactOrSearching: true)
7171

7272
let options = viewControllerMock.viewControllerPresented?.actions.compactMap { $0.title }
73-
XCTAssertEqual(["Cancel", "View", "Stats", "Share", "Duplicate", "Move to Draft", "Move to Trash"], options)
73+
XCTAssertEqual(["Cancel", "View", "Stats", "Share", "Duplicate", "Move to Draft", "Copy Link", "Move to Trash"], options)
7474
}
7575

7676
func testCallDelegateWhenStatsTapped() {
@@ -118,6 +118,15 @@ class PostActionSheetTests: XCTestCase {
118118
XCTAssertTrue(interactivePostViewDelegateMock.didCallHandleTrashPost)
119119
}
120120

121+
func testCallDelegateWhenCopyLink() {
122+
let viewModel = PostCardStatusViewModel(post: PostBuilder().published().build())
123+
124+
postActionSheet.show(for: viewModel, from: view)
125+
tap("Copy Link", in: viewControllerMock.viewControllerPresented)
126+
127+
XCTAssertTrue(interactivePostViewDelegateMock.didCallCopyLink)
128+
}
129+
121130
func testCallDelegateWhenMoveToTrashTapped() {
122131
let viewModel = PostCardStatusViewModel(post: PostBuilder().published().build())
123132

@@ -188,6 +197,7 @@ class InteractivePostViewDelegateMock: InteractivePostViewDelegate {
188197
private(set) var didCallRetry = false
189198
private(set) var didCallCancelAutoUpload = false
190199
private(set) var didCallShare = false
200+
private(set) var didCallCopyLink = false
191201

192202
func stats(for post: AbstractPost) {
193203
didCallHandleStats = true
@@ -232,4 +242,8 @@ class InteractivePostViewDelegateMock: InteractivePostViewDelegate {
232242
func share(_ post: AbstractPost, fromView view: UIView) {
233243
didCallShare = true
234244
}
245+
246+
func copyLink(_ post: AbstractPost) {
247+
didCallCopyLink = true
248+
}
235249
}

WordPress/WordPressTest/ViewRelated/Post/Views/PostCardStatusViewModelTests.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,48 +27,48 @@ class PostCardStatusViewModelTests: XCTestCase {
2727
(
2828
"Draft with remote",
2929
PostBuilder(context).drafted().withRemote().build(),
30-
ButtonGroups(primary: [.edit, .view, .more], secondary: [.publish, .duplicate, .trash])
30+
ButtonGroups(primary: [.edit, .view, .more], secondary: [.publish, .duplicate, .copyLink, .trash])
3131
),
3232
(
3333
"Draft that was not uploaded to the server",
3434
PostBuilder(context).drafted().with(remoteStatus: .failed).build(),
35-
ButtonGroups(primary: [.edit, .publish, .more], secondary: [.duplicate, .trash])
35+
ButtonGroups(primary: [.edit, .publish, .more], secondary: [.duplicate, .copyLink, .trash])
3636
),
3737
(
3838
"Draft with remote and confirmed local changes",
3939
PostBuilder(context).drafted().withRemote().with(remoteStatus: .failed).confirmedAutoUpload().build(),
40-
ButtonGroups(primary: [.edit, .cancelAutoUpload, .more], secondary: [.publish, .duplicate, .trash])
40+
ButtonGroups(primary: [.edit, .cancelAutoUpload, .more], secondary: [.publish, .duplicate, .copyLink, .trash])
4141
),
4242
(
4343
"Draft with remote and canceled local changes",
4444
PostBuilder(context).drafted().withRemote().with(remoteStatus: .failed).confirmedAutoUpload().cancelledAutoUpload().build(),
45-
ButtonGroups(primary: [.edit, .publish, .more], secondary: [.duplicate, .trash])
45+
ButtonGroups(primary: [.edit, .publish, .more], secondary: [.duplicate, .copyLink, .trash])
4646
),
4747
(
4848
"Local published draft with confirmed auto-upload",
4949
PostBuilder(context).published().with(remoteStatus: .failed).confirmedAutoUpload().build(),
50-
ButtonGroups(primary: [.edit, .cancelAutoUpload, .more], secondary: [.duplicate, .moveToDraft, .trash])
50+
ButtonGroups(primary: [.edit, .cancelAutoUpload, .more], secondary: [.duplicate, .moveToDraft, .copyLink, .trash])
5151
),
5252
(
5353
"Local published draft with canceled auto-upload",
5454
PostBuilder(context).published().with(remoteStatus: .failed).build(),
55-
ButtonGroups(primary: [.edit, .publish, .more], secondary: [.duplicate, .moveToDraft, .trash])
55+
ButtonGroups(primary: [.edit, .publish, .more], secondary: [.duplicate, .moveToDraft, .copyLink, .trash])
5656
),
5757
(
5858
"Published post",
5959
PostBuilder(context).published().withRemote().build(),
60-
ButtonGroups(primary: [.edit, .view, .more], secondary: [.stats, .share, .duplicate, .moveToDraft, .trash])
60+
ButtonGroups(primary: [.edit, .view, .more], secondary: [.stats, .share, .duplicate, .moveToDraft, .copyLink, .trash])
6161
),
6262
(
6363
"Published post with local confirmed changes",
6464
PostBuilder(context).published().withRemote().with(remoteStatus: .failed).confirmedAutoUpload().build(),
65-
ButtonGroups(primary: [.edit, .cancelAutoUpload, .more], secondary: [.stats, .share, .duplicate, .moveToDraft, .trash])
65+
ButtonGroups(primary: [.edit, .cancelAutoUpload, .more], secondary: [.stats, .share, .duplicate, .moveToDraft, .copyLink, .trash])
6666
),
6767
(
6868
"Post with the max number of auto uploades retry reached",
6969
PostBuilder(context).with(remoteStatus: .failed)
7070
.with(autoUploadAttemptsCount: 3).confirmedAutoUpload().build(),
71-
ButtonGroups(primary: [.edit, .retry, .more], secondary: [.publish, .duplicate, .moveToDraft, .trash])
71+
ButtonGroups(primary: [.edit, .retry, .more], secondary: [.publish, .duplicate, .moveToDraft, .copyLink, .trash])
7272
),
7373
]
7474

0 commit comments

Comments
 (0)