Skip to content

Commit c6301e1

Browse files
authored
Merge pull request #583 from woocommerce/issue/341-copy-address
Order Details: allow copying of the shipping/billing address
2 parents 99c8cc1 + 407f17b commit c6301e1

File tree

2 files changed

+136
-12
lines changed

2 files changed

+136
-12
lines changed

WooCommerce/Classes/Model/Address+Woo.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,19 @@ extension Address {
3535
return postalAddress.formatted(as: .mailingAddress)
3636
}
3737

38+
/// Returns the `fullName`, `company`, and `address`. Basically this var combines the
39+
/// `fullNameWithCompany` & `formattedPostalAddress` vars.
40+
///
41+
var fullNameWithCompanyAndAddress: String {
42+
var output: [String] = [fullNameWithCompany]
43+
44+
if let formattedPostalAddress = formattedPostalAddress, formattedPostalAddress.isEmpty == false {
45+
output.append(formattedPostalAddress)
46+
}
47+
48+
return output.joined(separator: "\n")
49+
}
50+
3851
/// Returns the (clean) Phone number: contains only decimal digits.
3952
///
4053
var cleanedPhoneNumber: String? {

WooCommerce/Classes/ViewRelated/Orders/OrderDetailsViewController.swift

Lines changed: 123 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ class OrderDetailsViewController: UIViewController {
5858
}
5959
}
6060

61+
/// Haptic Feedback!
62+
///
63+
private let hapticGenerator = UINotificationFeedbackGenerator()
64+
6165

6266
// MARK: - View Lifecycle
6367

@@ -437,18 +441,6 @@ private extension OrderDetailsViewController {
437441

438442
cell.display(orderStatus: viewModel.order.status)
439443
}
440-
441-
// MARK: - Get order note
442-
//
443-
func note(at indexPath: IndexPath) -> OrderNote? {
444-
// We need to subtract 1 here because the first order note row is the "Add Order" cell
445-
let noteIndex = indexPath.row - 1
446-
guard orderNotes.indices.contains(noteIndex) else {
447-
return nil
448-
}
449-
450-
return orderNotes[noteIndex]
451-
}
452444
}
453445

454446

@@ -589,6 +581,7 @@ extension OrderDetailsViewController: UITableViewDataSource {
589581
// MARK: - UITableViewDelegate Conformance
590582
//
591583
extension OrderDetailsViewController: UITableViewDelegate {
584+
592585
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
593586
tableView.deselectRow(at: indexPath, animated: true)
594587

@@ -609,6 +602,51 @@ extension OrderDetailsViewController: UITableViewDelegate {
609602
}
610603
}
611604

605+
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
606+
guard checkIfCopyingIsAllowed(for: indexPath) else {
607+
// Only allow the leading swipe action on the address rows
608+
return UISwipeActionsConfiguration(actions: [])
609+
}
610+
611+
let row = rowAtIndexPath(indexPath)
612+
let copyActionTitle = NSLocalizedString("Copy", comment: "Copy address text button title — should be one word and as short as possible.")
613+
let copyAction = UIContextualAction(style: .normal, title: copyActionTitle) { [weak self] (action, view, success) in
614+
self?.copyText(at: row)
615+
success(true)
616+
}
617+
copyAction.backgroundColor = StyleManager.wooCommerceBrandColor
618+
619+
return UISwipeActionsConfiguration(actions: [copyAction])
620+
}
621+
622+
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
623+
// No trailing action on any cell
624+
return UISwipeActionsConfiguration(actions: [])
625+
}
626+
627+
func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool {
628+
return checkIfCopyingIsAllowed(for: indexPath)
629+
}
630+
631+
func tableView(_ tableView: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
632+
return action == #selector(copy(_:))
633+
}
634+
635+
func tableView(_ tableView: UITableView, performAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) {
636+
guard action == #selector(copy(_:)) else {
637+
return
638+
}
639+
640+
let row = rowAtIndexPath(indexPath)
641+
copyText(at: row)
642+
}
643+
}
644+
645+
646+
// MARK: - Segues
647+
//
648+
extension OrderDetailsViewController {
649+
612650
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
613651
if let productListViewController = segue.destination as? ProductListViewController {
614652
productListViewController.viewModel = viewModel
@@ -617,6 +655,79 @@ extension OrderDetailsViewController: UITableViewDelegate {
617655
}
618656

619657

658+
// MARK: - Convenience Methods
659+
//
660+
private extension OrderDetailsViewController {
661+
662+
func rowAtIndexPath(_ indexPath: IndexPath) -> Row {
663+
return sections[indexPath.section].rows[indexPath.row]
664+
}
665+
666+
func note(at indexPath: IndexPath) -> OrderNote? {
667+
// We need to subtract 1 here because the first order note row is the "Add Order" cell
668+
let noteIndex = indexPath.row - 1
669+
guard orderNotes.indices.contains(noteIndex) else {
670+
return nil
671+
}
672+
673+
return orderNotes[noteIndex]
674+
}
675+
676+
/// Checks if copying the row data at the provided indexPath is allowed
677+
///
678+
/// - Parameter indexPath: indexpath of the row to check
679+
/// - Returns: true is copying is allowed, false otherwise
680+
///
681+
func checkIfCopyingIsAllowed(for indexPath: IndexPath) -> Bool {
682+
let row = rowAtIndexPath(indexPath)
683+
switch row {
684+
case .billingAddress:
685+
if let _ = viewModel.order.billingAddress {
686+
return true
687+
}
688+
case .shippingAddress:
689+
if let _ = viewModel.order.shippingAddress {
690+
return true
691+
}
692+
default:
693+
break
694+
}
695+
696+
return false
697+
}
698+
699+
/// Sends the provided Row's text data to the pasteboard
700+
///
701+
/// - Parameter row: Row to copy text data from
702+
///
703+
func copyText(at row: Row) {
704+
switch row {
705+
case .billingAddress:
706+
sendToPasteboard(viewModel.order.billingAddress?.fullNameWithCompanyAndAddress)
707+
case .shippingAddress:
708+
sendToPasteboard(viewModel.order.shippingAddress?.fullNameWithCompanyAndAddress)
709+
default:
710+
break // We only send text to the pasteboard from the address rows right meow
711+
}
712+
}
713+
714+
/// Sends the provided text to the general pasteboard and triggers a success haptic. If the text param
715+
/// is nil, nothing is sent to the pasteboard.
716+
///
717+
/// - Parameter text: string value to send to the pasteboard
718+
///
719+
func sendToPasteboard(_ text: String?) {
720+
guard let text = text, text.isEmpty == false else {
721+
return
722+
}
723+
724+
// Insert an extra newline to make life easier when pasting
725+
UIPasteboard.general.string = text + "\n"
726+
hapticGenerator.notificationOccurred(.success)
727+
}
728+
}
729+
730+
620731
// MARK: - Contact Alert
621732
//
622733
private extension OrderDetailsViewController {

0 commit comments

Comments
 (0)