Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Shipping labels] Print shipping label when print button is tapped #14545

Merged
merged 3 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import SwiftUI
struct WooShippingPostPurchaseView: View {
@ObservedObject private(set) var viewModel: WooShippingPostPurchaseViewModel

@State private var isPrintingLabel = false

var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text(Localization.readyToPrint)
Expand Down Expand Up @@ -35,12 +37,16 @@ struct WooShippingPostPurchaseView: View {
.roundedBorder(cornerRadius: 8, lineColor: Color(.separator), lineWidth: 1)
}
Button {
// TODO: Request label from remote and open print dialog
isPrintingLabel = true
Task { @MainActor in
await viewModel.printLabel()
isPrintingLabel = false
}
} label: {
Text(Localization.printButton)
.bold()
}
.buttonStyle(HighlightButtonStyle(background: Layout.panelHighlight, backgroundPressed: Layout.buttonPressed))
.buttonStyle(HighlightLoadingButtonStyle(isLoading: isPrintingLabel, background: Layout.panelHighlight, backgroundPressed: Layout.buttonPressed))
NavigationLink {
ShippingLabelPrintingInstructionsView()
.navigationTitle(Localization.infoTitle)
Expand Down Expand Up @@ -144,13 +150,19 @@ private extension WooShippingPostPurchaseView {
}

#Preview {
WooShippingPostPurchaseView(viewModel: WooShippingPostPurchaseViewModel(labelSizes: [.label, .legal, .a4],
WooShippingPostPurchaseView(viewModel: WooShippingPostPurchaseViewModel(siteID: 123,
labelID: 1,
labelSizes: [.label, .legal, .a4],
trackingURL: URL(string: "https://woocommerce.com"),
pickupURL: WooShippingCarrier.usps.pickupURL))
.padding()
}

#Preview("Label without links") {
WooShippingPostPurchaseView(viewModel: WooShippingPostPurchaseViewModel(labelSizes: [.label, .legal, .a4], trackingURL: nil, pickupURL: nil))
WooShippingPostPurchaseView(viewModel: WooShippingPostPurchaseViewModel(siteID: 123,
labelID: 1,
labelSizes: [.label, .legal, .a4],
trackingURL: nil,
pickupURL: nil))
.padding()
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import Yosemite
import WooFoundation

final class WooShippingPostPurchaseViewModel: ObservableObject {
private let stores: StoresManager
private let siteID: Int64
private let labelID: Int64

/// Available paper sizes for printing the shipping label.
let labelSizes: [ShippingLabelPaperSize]

Expand All @@ -14,16 +18,23 @@ final class WooShippingPostPurchaseViewModel: ObservableObject {
/// Shipment pickup URL for the shipping label.
let pickupURL: URL?

init(labelSizes: [ShippingLabelPaperSize],
init(siteID: Int64,
labelID: Int64,
labelSizes: [ShippingLabelPaperSize],
trackingURL: URL?,
pickupURL: URL?) {
pickupURL: URL?,
stores: StoresManager = ServiceLocator.stores) {
self.siteID = siteID
self.labelID = labelID
self.labelSizes = labelSizes
self.trackingURL = trackingURL
self.pickupURL = pickupURL
self.stores = stores
}

convenience init(shippingLabel: ShippingLabel,
siteAddress: SiteAddress = SiteAddress()) {
siteAddress: SiteAddress = SiteAddress(),
stores: StoresManager = ServiceLocator.stores) {
// Label sizes aren't provided by the API, so we can hard-code them to match the extension behavior:
let labelSizes = {
var availableLabelSizes: [ShippingLabelPaperSize] = [.label, .letter]
Expand All @@ -35,8 +46,42 @@ final class WooShippingPostPurchaseViewModel: ObservableObject {
let trackingURL = ShippingLabelTrackingURLGenerator.url(for: shippingLabel)
let pickupURL = WooShippingCarrier(rawValue: shippingLabel.carrierID)?.pickupURL

self.init(labelSizes: labelSizes,
self.init(siteID: shippingLabel.siteID,
labelID: shippingLabel.shippingLabelID,
labelSizes: labelSizes,
trackingURL: trackingURL,
pickupURL: pickupURL)
pickupURL: pickupURL,
stores: stores)
}

/// Fetches the shipping label in the selected paper size and presents the print dialog.
@MainActor
func printLabel() async {
do {
let printData = try await requestPrintData()
presentPrintDialog(with: printData)
} catch {
DDLogError("Error generating shipping label document for printing: \(error)")
}
}
}

private extension WooShippingPostPurchaseViewModel {
/// Requests the shipping label data for printing.
@MainActor
func requestPrintData() async throws -> ShippingLabelPrintData {
try await withCheckedThrowingContinuation { continuation in
let action = WooShippingAction.printLabel(siteID: siteID, labelIDs: [labelID], paperSize: selectedLabelSize) { result in
continuation.resume(with: result)
}
stores.dispatch(action)
}
}

/// Presents the print dialog with the provided print data.
func presentPrintDialog(with printData: ShippingLabelPrintData) {
let printController = UIPrintInteractionController()
printController.printingItem = printData.data
printController.present(animated: true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,46 @@ struct HighlightButtonStyle: ButtonStyle {
/// Background color when the button is pressed.
let backgroundPressed: Color

/// Defines if the content should be hidden.
/// Useful for when we want to show an overlay on top of the bottom without hiding its decoration. Like showing a progress view.
///
private(set) var hideContent = false

func makeBody(configuration: Configuration) -> some View {
HighlightButton(configuration: configuration, background: background, backgroundPressed: backgroundPressed)
HighlightButton(configuration: configuration, background: background, backgroundPressed: backgroundPressed, hideContent: hideContent)
}
}

/// Adds a highlight button style while showing a progress view on top of the button when required.
///
struct HighlightLoadingButtonStyle: PrimitiveButtonStyle {
/// Set to `true` to show a progress view within the button.
var isLoading: Bool

/// Background color for the button.
let background: Color

/// Background color when the button is pressed.
let backgroundPressed: Color

/// Returns a `ProgressView` if the view is loading. Return nil otherwise
///
private var progressViewOverlay: ProgressView<EmptyView, EmptyView>? {
isLoading ? ProgressView() : nil
}

func makeBody(configuration: Configuration) -> some View {
return Button(configuration)
.buttonStyle(HighlightButtonStyle(background: background, backgroundPressed: backgroundPressed, hideContent: isLoading))
.disabled(isLoading)
.overlay(progressViewOverlay.tint(Color(.primaryButtonTitle)))
}

/// Only dispatch events while the view is not loading.
///
private func dispatchTrigger(_ configuration: Configuration) {
guard !isLoading else { return }
configuration.trigger()
}
}

Expand Down Expand Up @@ -481,8 +519,14 @@ private struct HighlightButton: View {
/// Background color when the button is pressed.
let backgroundPressed: Color

/// Defines if the content should be hidden.
/// Useful for when we want to show an overlay on top of the bottom without hiding its decoration. Like showing a progress view.
///
private(set) var hideContent = false

var body: some View {
BaseButton(configuration: configuration)
.opacity(contentOpacity)
.foregroundColor(Color(.primaryButtonTitle))
.font(.headline)
.background(
Expand All @@ -498,6 +542,10 @@ private struct HighlightButton: View {
return background
}
}

var contentOpacity: Double {
hideContent ? 0.0 : 1.0
}
}

private enum Style {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class WooShippingPostPurchaseViewModelTests: XCTestCase {
let pickupURL = WooShippingCarrier.usps.pickupURL

// When
let viewModel = WooShippingPostPurchaseViewModel(labelSizes: labelSizes, trackingURL: trackingURL, pickupURL: pickupURL)
let viewModel = WooShippingPostPurchaseViewModel(siteID: 123, labelID: 1, labelSizes: labelSizes, trackingURL: trackingURL, pickupURL: pickupURL)

// Then
XCTAssertEqual(viewModel.labelSizes, labelSizes)
Expand Down Expand Up @@ -68,4 +68,27 @@ final class WooShippingPostPurchaseViewModelTests: XCTestCase {
XCTAssertEqual(viewModel.pickupURL, WooShippingCarrier.usps.pickupURL)
}

@MainActor
func test_printLabel_fetches_label_data_from_remote() async {
// Given
var printData: ShippingLabelPrintData?
let stores = MockStoresManager(sessionManager: .testingInstance)
stores.whenReceivingAction(ofType: WooShippingAction.self) { action in
switch action {
case let .printLabel(_, _, _, completion):
let data = ShippingLabelPrintData.fake()
printData = data
completion(.success(data))
default:
XCTFail("Unexpected action: \(action)")
}
}
let viewModel = WooShippingPostPurchaseViewModel(shippingLabel: ShippingLabel.fake(), stores: stores)

// When
await viewModel.printLabel()

// Then
XCTAssertNotNil(printData)
}
}