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 - Customs Form] UI 3 - Information Dialogs & Countries #14770

Merged
merged 8 commits into from
Jan 7, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,7 @@ struct WooShippingCustomsForm: View {
hsTariffNumber: "HS 14-1",
valuePerUnit: "$20.00",
weightPerUnit: "0.3kg",
originCountry: Country(code: "US", name: "United States", states: []),
allCountries: [])
originCountry: WooShippingCustomsCountry(code: "US", name: "United States"))
)
}
.padding()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ struct WooShippingCustomsItem: View {
@ObservedObject var viewModel: WooShippingCustomsItemViewModel
@State private var isShowingHSTarrifInfoWebView = false
@State private var isShowingCountries = false
@State private var isShowingDescriptionInfoDialog = false
@State private var isShowingOriginCountryInfoDialog = false


@Environment(\.shippingWeightUnit) var weightUnit: String

Expand Down Expand Up @@ -52,7 +55,7 @@ struct WooShippingCustomsItem: View {
.subheadlineStyle()
Spacer()
Button {
// TODO: Add information
isShowingDescriptionInfoDialog = true
} label: {
Image(systemName: "info.circle")
.foregroundColor(Color(.wooCommercePurple(.shade60)))
Expand Down Expand Up @@ -127,7 +130,7 @@ struct WooShippingCustomsItem: View {
.foregroundColor(.primary)
Spacer()
Button {
// TODO: Add information
isShowingOriginCountryInfoDialog = true
} label: {
Image(systemName: "info.circle")
.foregroundColor(Color(.wooCommercePurple(.shade60)))
Expand Down Expand Up @@ -158,12 +161,20 @@ struct WooShippingCustomsItem: View {
.sheet(isPresented: $isShowingCountries, content: {
NavigationStack {
SingleSelectionList(title: Localization.originCountryTitle,
items: viewModel.allCountries,
items: viewModel.countries,
contentKeyPath: \.name,
selected: $viewModel.originCountry)
Comment on lines 163 to 166
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you think of using the FilterListSelector view instead of SingleSelectionList, so the merchant can search for the country they want to select?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a good idea! As using FilterListSelector is not so straightforward in this case, I created an issue so we can address this later on.

}
.wooNavigationBarStyle()
})
.fullScreenCover(isPresented: $isShowingDescriptionInfoDialog) {
WooShippingCustomsItemDescriptionInfoDialog()
.background(FullScreenCoverClearBackgroundView())
}
.fullScreenCover(isPresented: $isShowingOriginCountryInfoDialog) {
WooShippingCustomsItemOriginCountryInfoDialog()
.background(FullScreenCoverClearBackgroundView())
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import SwiftUI

struct WooShippingCustomsItemDescriptionInfoDialog: View {
/// Scale of the view based on accessibility changes
@ScaledMetric private var scale: CGFloat = 1.0

@Environment(\.dismiss) var dismiss

/// Whether the learn more webview is being shown.
@State private var showLearnMoreWebView: Bool = false

var body: some View {
ZStack {
Color.black.opacity(Layout.backgroundOpacity).edgesIgnoringSafeArea(.all)

VStack {
GeometryReader { geometry in
ScrollView {
VStack(alignment: .center, spacing: Layout.verticalSpacing) {
Text(Localization.title)
.headlineStyle()
Text(Localization.bodyParagraph)
.bodyStyle()
.fixedSize(horizontal: false, vertical: true)

Button {
showLearnMoreWebView = true
} label: {
Label {
Text(Localization.learnMoreButtonTitle)
.font(.body)
.fontWeight(.bold)
} icon: {
Image(systemName: "arrow.up.forward.square")
.resizable()
.frame(width: Layout.externalLinkImageSize * scale, height: Layout.externalLinkImageSize * scale)
}
}
.buttonStyle(PrimaryButtonStyle())
.safariSheet(isPresented: $showLearnMoreWebView, url: WooConstants.URLs.shippingCustomsInstructionsForEUCountries.asURL())

Button {
dismiss()
} label: {
Text(Localization.doneButtonTitle)
}
.buttonStyle(SecondaryButtonStyle())
}
.padding(Layout.outterPadding)
.frame(maxWidth: .infinity, alignment: .center)
.background(Color(.systemBackground))
.cornerRadius(Layout.cornerRadius)
.frame(width: geometry.size.width)
.frame(minHeight: geometry.size.height)
}
}
}
.padding(Layout.outterPadding)
.frame(maxWidth: .infinity, alignment: .center)
}
}
}
extension WooShippingCustomsItemDescriptionInfoDialog {
enum Localization {
static let title = NSLocalizedString("shipping.customs.descriptionInfoDialogTitle",
value: "Description",
comment: "Title for the custom description educational dialog")
static let bodyParagraph = NSLocalizedString("shipping.customs.descriptionInfoDialogBody",
value: "When shipping to countries that follow European Union (EU) customs rules, " +
"you must provide a clear, specific description on every item. " +
"For example, if you are sending clothing, you must indicate what type of clothing" +
" (e.g. men's shirts, girl's vest, boy's jacket) for the description to be acceptable." +
" Otherwise, shipments may be delayed or interrupted at customs.",
comment: "Body for the custom items description educational dialog")
static let learnMoreButtonTitle = NSLocalizedString("shipping.customs.descriptionInfoDialogLearnMore",
value: "Learn more",
comment: "Button title for the learn more action in the custom descriptions info dialog")
static let doneButtonTitle = NSLocalizedString("shipping.customs.descriptionInfoDialogDone",
value: "Done",
comment: "Button title for the done button in the customs description educational dialog")
}
enum Layout {
static let backgroundOpacity: CGFloat = 0.5
static let externalLinkImageSize: CGFloat = 18
static let verticalSpacing: CGFloat = 16
static let outterPadding: CGFloat = 24
static let cornerRadius: CGFloat = 8
static let dividerHeight: CGFloat = 1
static let taxLinesInnerSpacing: CGFloat = 4
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import SwiftUI

struct WooShippingCustomsItemOriginCountryInfoDialog: View {
/// Scale of the view based on accessibility changes
@ScaledMetric private var scale: CGFloat = 1.0

@Environment(\.dismiss) var dismiss

var body: some View {
ZStack {
Color.black.opacity(Layout.backgroundOpacity).edgesIgnoringSafeArea(.all)

VStack {
GeometryReader { geometry in
ScrollView {
VStack(alignment: .center, spacing: Layout.verticalSpacing) {
Text(Localization.title)
.headlineStyle()
Text(Localization.bodyParagraph)
.bodyStyle()
.fixedSize(horizontal: false, vertical: true)
Button {
dismiss()
} label: {
Text(Localization.doneButtonTitle)
}
.buttonStyle(PrimaryButtonStyle())
}
.padding(Layout.outterPadding)
.frame(maxWidth: .infinity, alignment: .center)
.background(Color(.systemBackground))
.cornerRadius(Layout.cornerRadius)
.frame(width: geometry.size.width)
.frame(minHeight: geometry.size.height)
}
}
}
.padding(Layout.outterPadding)
.frame(maxWidth: .infinity, alignment: .center)
}
}
}
extension WooShippingCustomsItemOriginCountryInfoDialog {
enum Localization {
static let title = NSLocalizedString("shipping.customs.originCountryInfoDialogTitle",
value: "Origin Country",
comment: "Title for the custom origin country educational dialog")
static let bodyParagraph = NSLocalizedString("shipping.customs.originCountryInfoDialogBody",
value: "Country where the product was manufactured or assembled.",
comment: "Body for the custom items origin country educational dialog")
static let doneButtonTitle = NSLocalizedString("shipping.customs.originCountryInfoDialogDoneButton",
value: "Done",
comment: "Button title for the done button in the customs description educational dialog")
}
enum Layout {
static let backgroundOpacity: CGFloat = 0.5
static let externalLinkImageSize: CGFloat = 18
static let verticalSpacing: CGFloat = 16
static let outterPadding: CGFloat = 24
static let cornerRadius: CGFloat = 8
static let dividerHeight: CGFloat = 1
static let taxLinesInnerSpacing: CGFloat = 4
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,39 @@
import Yosemite
import SwiftUI
import protocol Storage.StorageManagerType

struct WooShippingCustomsCountry: Hashable {
let code: String
let name: String
}

final class WooShippingCustomsItemViewModel: ObservableObject {
@Published var title: String
@Published var description: String
@Published var hsTariffNumber: String
@Published var valuePerUnit: String
@Published var weightPerUnit: String
@Published var originCountry: Country
@Published var originCountry: WooShippingCustomsCountry

var informationIsMissing: Bool = true

let allCountries: [Country]
private let storageManager: StorageManagerType
private let stores: StoresManager
private let siteID: Int64

private lazy var resultsController: ResultsController<StorageCountry> = {
let descriptor = NSSortDescriptor(key: "name", ascending: true)
return ResultsController(storageManager: storageManager, matching: nil, sortedBy: [descriptor])
}()

var countries: [WooShippingCustomsCountry] {
let countries = resultsController.fetchedObjects

// This removes the states property because:
// - It's not necessary to display the list
// - As we retrieve a different order on the states array property from the ResultsController, it might mess the Equality comparison
return countries.map { WooShippingCustomsCountry(code: $0.code, name: $0.name) }
}

let hsTariffURL = WooConstants.URLs.hsTariffURL.asURL()

Expand All @@ -20,14 +42,36 @@ final class WooShippingCustomsItemViewModel: ObservableObject {
hsTariffNumber: String,
valuePerUnit: String,
weightPerUnit: String,
originCountry: Country,
allCountries: [Country]) {
originCountry: WooShippingCustomsCountry,
storageManager: StorageManagerType = ServiceLocator.storageManager,
stores: StoresManager = ServiceLocator.stores) {
self.title = title
self.description = description
self.hsTariffNumber = hsTariffNumber
self.valuePerUnit = valuePerUnit
self.weightPerUnit = weightPerUnit
self.originCountry = originCountry
self.allCountries = allCountries
self.storageManager = storageManager
self.stores = stores
self.siteID = stores.sessionManager.defaultStoreID ?? Int64.min

fetchCountries()
}
}

extension WooShippingCustomsItemViewModel {
func fetchCountries() {
try? resultsController.performFetch()
let action = DataAction.synchronizeCountries(siteID: siteID) { [weak self] (result) in
guard let self = self else { return }
switch result {
case .success:
try? self.resultsController.performFetch()
case .failure:
break
}
}

stores.dispatch(action)
}
}
8 changes: 8 additions & 0 deletions WooCommerce/WooCommerce.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1992,6 +1992,8 @@
B90D21782D15B72900ED60ED /* WooShippingCustomsFormViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B90D21772D15B72700ED60ED /* WooShippingCustomsFormViewModel.swift */; };
B90D217A2D1B06D000ED60ED /* WooShippingCustomsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B90D21792D1B06CE00ED60ED /* WooShippingCustomsItem.swift */; };
B90D217C2D1B06F700ED60ED /* WooShippingCustomsItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B90D217B2D1B06F600ED60ED /* WooShippingCustomsItemViewModel.swift */; };
B90D217E2D1ECA3100ED60ED /* WooShippingCustomsItemDescriptionInfoDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = B90D217D2D1ECA1F00ED60ED /* WooShippingCustomsItemDescriptionInfoDialog.swift */; };
B90D21802D1ED1F300ED60ED /* WooShippingCustomsItemOriginCountryInfoDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = B90D217F2D1ED1DE00ED60ED /* WooShippingCustomsItemOriginCountryInfoDialog.swift */; };
B90DACC02A30AEF000365897 /* BarcodeScannerItemFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B90DACBF2A30AEF000365897 /* BarcodeScannerItemFinder.swift */; };
B90DACC22A31BBC800365897 /* BarcodeScannerProductFinderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B90DACC12A31BBC800365897 /* BarcodeScannerProductFinderTests.swift */; };
B90DD08E2D12FAA400EFC06A /* WooShippingCustomsRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B90DD08D2D12FA9900EFC06A /* WooShippingCustomsRow.swift */; };
Expand Down Expand Up @@ -5127,6 +5129,8 @@
B90D21772D15B72700ED60ED /* WooShippingCustomsFormViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooShippingCustomsFormViewModel.swift; sourceTree = "<group>"; };
B90D21792D1B06CE00ED60ED /* WooShippingCustomsItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooShippingCustomsItem.swift; sourceTree = "<group>"; };
B90D217B2D1B06F600ED60ED /* WooShippingCustomsItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooShippingCustomsItemViewModel.swift; sourceTree = "<group>"; };
B90D217D2D1ECA1F00ED60ED /* WooShippingCustomsItemDescriptionInfoDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooShippingCustomsItemDescriptionInfoDialog.swift; sourceTree = "<group>"; };
B90D217F2D1ED1DE00ED60ED /* WooShippingCustomsItemOriginCountryInfoDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooShippingCustomsItemOriginCountryInfoDialog.swift; sourceTree = "<group>"; };
B90DACBF2A30AEF000365897 /* BarcodeScannerItemFinder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarcodeScannerItemFinder.swift; sourceTree = "<group>"; };
B90DACC12A31BBC800365897 /* BarcodeScannerProductFinderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarcodeScannerProductFinderTests.swift; sourceTree = "<group>"; };
B90DD08D2D12FA9900EFC06A /* WooShippingCustomsRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooShippingCustomsRow.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -10916,6 +10920,8 @@
B90DD08C2D12FA6600EFC06A /* WooShipping Customs */ = {
isa = PBXGroup;
children = (
B90D217F2D1ED1DE00ED60ED /* WooShippingCustomsItemOriginCountryInfoDialog.swift */,
B90D217D2D1ECA1F00ED60ED /* WooShippingCustomsItemDescriptionInfoDialog.swift */,
B90D217B2D1B06F600ED60ED /* WooShippingCustomsItemViewModel.swift */,
B90D21792D1B06CE00ED60ED /* WooShippingCustomsItem.swift */,
B90D21772D15B72700ED60ED /* WooShippingCustomsFormViewModel.swift */,
Expand Down Expand Up @@ -15147,6 +15153,7 @@
029F29FC24D94106004751CA /* EditableProductVariationModel.swift in Sources */,
0218B4EC242E06F00083A847 /* MediaType+WPMediaType.swift in Sources */,
D85A3C5026C153A500C0E026 /* InPersonPaymentsPluginNotActivatedView.swift in Sources */,
B90D217E2D1ECA3100ED60ED /* WooShippingCustomsItemDescriptionInfoDialog.swift in Sources */,
D8815AE726383FD600EDAD62 /* CardPresentPaymentsModalViewModel.swift in Sources */,
DAB4099F2CA5A329008EE1F2 /* WooShippingAddPackageView.swift in Sources */,
02B21C5729C9EEF900C5623B /* WooAnalyticsEvent+StoreOnboarding.swift in Sources */,
Expand Down Expand Up @@ -16210,6 +16217,7 @@
261AA30C2753119E009530FE /* PaymentMethodsViewModel.swift in Sources */,
68E674AD2A4DAC010034BA1E /* CurrentPlanDetailsView.swift in Sources */,
DE68B81F26F86B1700C86CFB /* OfflineBannerView.swift in Sources */,
B90D21802D1ED1F300ED60ED /* WooShippingCustomsItemOriginCountryInfoDialog.swift in Sources */,
D8610BCC256F284700A5DF27 /* ULErrorViewModel.swift in Sources */,
CCFC50552743BC0D001E505F /* OrderForm.swift in Sources */,
262418332B8D3630009A3834 /* ApplicationPasswordTutorial.swift in Sources */,
Expand Down
Loading