Skip to content
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
@@ -0,0 +1,16 @@
import Foundation

/// DateFormatter Extensions
///
extension DateFormatter {
/// Date formatter used for creating a **localized** date and time string.
///
/// Example output in English: "Jan 28, 2018, 11:23 AM"
///
public static let dateAndTimeFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.setLocalizedDateFormatFromTemplate("MMM d yyyy hh:mm a")

return formatter
}()
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
import Foundation

extension Decimal {

/// Returns the int value of a decimal. We ensure we round up our Decimal before converting it to an Int, using NSDecimalRound.
///
var intValue: Int {
public var intValue: Int {
NSDecimalNumber(decimal: whole).intValue
}

func rounded(_ roundingMode: NSDecimalNumber.RoundingMode = .up, scale: Int = 0) -> Self {
public func rounded(_ roundingMode: NSDecimalNumber.RoundingMode = .up, scale: Int = 0) -> Self {
var result = Self()
var number = self
NSDecimalRound(&result, &number, scale, roundingMode)
return result
}

private var whole: Self { rounded( self < 0 ? .down : .up) }

private var fraction: Self { self - whole }

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ import SwiftUI
import SafariServices
import UIKit

struct SafariSheetView: UIViewControllerRepresentable {
let url: URL
public struct SafariSheetView: UIViewControllerRepresentable {
private let url: URL

func makeUIViewController(context: Context) -> UIViewController {
public init(url: URL) {
self.url = url
}

public func makeUIViewController(context: Context) -> UIViewController {
SFSafariViewController(url: url)
}

func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
public func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
// nothing to do here
}
}
Expand All @@ -20,7 +24,7 @@ extension View {
/// Does nothing if the input URL is nil.
///
@ViewBuilder
func safariSheet(isPresented: Binding<Bool>, url: URL?, onDismiss: (() -> Void)? = nil) -> some View {
public func safariSheet(isPresented: Binding<Bool>, url: URL?, onDismiss: (() -> Void)? = nil) -> some View {
if let url = url {
sheet(isPresented: isPresented, onDismiss: onDismiss) {
SafariSheetView(url: url)
Expand All @@ -33,7 +37,7 @@ extension View {
///
/// When the sheet is dismissed, the binding's value will be set to nil.
///
func safariSheet(url: Binding<URL?>, onDismiss: (() -> Void)? = nil) -> some View {
public func safariSheet(url: Binding<URL?>, onDismiss: (() -> Void)? = nil) -> some View {
sheet(isPresented: url.notNil(), onDismiss: onDismiss) {
if let url = url.wrappedValue {
SafariSheetView(url: url)
Expand Down
23 changes: 23 additions & 0 deletions Modules/Sources/WooFoundation/UI Components/SafariView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import SwiftUI
import SafariServices

/// SwiftUI interface for UIKit SFSafariViewController
/// Provides a visible interface for web browsing, and Safari features
///
public struct SafariView: UIViewControllerRepresentable {

private let url: URL

public init(url: URL) {
self.url = url
}

public func makeUIViewController(context: UIViewControllerRepresentableContext<SafariView>) -> SFSafariViewController {
return SFSafariViewController(url: url)
}

public func updateUIViewController(_ uiViewController: SFSafariViewController,
context: UIViewControllerRepresentableContext<SafariView>) {

}
}
Original file line number Diff line number Diff line change
@@ -1,92 +1,13 @@
import Foundation
import Yosemite
import WooFoundation

/// Represent and parse the Address of the store, returned in the SiteSettings API `/settings/general/`
///
final class SiteAddress {

private let siteSettings: [SiteSetting]

var address: String {
return getValueFromSiteSettings(Constants.address) ?? ""
}

var address2: String {
return getValueFromSiteSettings(Constants.address2) ?? ""
}

var city: String {
return getValueFromSiteSettings(Constants.city) ?? ""
}

var postalCode: String {
return getValueFromSiteSettings(Constants.postalCode) ?? ""
}

var countryCode: CountryCode {
guard let countryComponent = getCountryAndStateComponents().first else {
DDLogError("⛔️ Could not determine country code for site address: no country component found.")
return .unknown
}
guard let countryCode = CountryCode(rawValue: countryComponent) else {
DDLogError("⛔️ Could not determine country code for country: \(countryComponent)")
return .unknown
}
return countryCode
}

/// Returns the name of the country associated with the current store.
/// The default store country is provided in a format like `HK:KOWLOON`
/// This method will transform `HK:KOWLOON` into `Hong Kong`
/// Will return nil if it can not figure out a valid country name
var countryName: String? {
guard countryCode != .unknown else {
return nil
}

return countryCode.readableCountry
}

var state: String {
return getCountryAndStateComponents().last ?? ""
}

init(siteSettings: [SiteSetting] = ServiceLocator.selectedSiteSettings.siteSettings) {
self.siteSettings = siteSettings
}

private func getCountryAndStateComponents() -> [String] {
getValueFromSiteSettings(Constants.countryAndState)?.components(separatedBy: ":") ?? []
}

private func getValueFromSiteSettings(_ settingID: String) -> String? {
return siteSettings.first { (setting) -> Bool in
return setting.settingID == settingID
}?.value
}
}

// MARK: - Constants.
//
private extension SiteAddress {
/// The key of the SiteSetting containing the store address
enum Constants {
static let address = "woocommerce_store_address"
static let address2 = "woocommerce_store_address_2"
static let city = "woocommerce_store_city"
static let postalCode = "woocommerce_store_postcode"
static let countryAndState = "woocommerce_default_country"
}
}

// MARK: - Mapping between country codes and readable names
// The country names were extracted from the response to `/wp-json/wc/v3/settings/general`
// The default countries are listed under `woocommerce_default_country`
// in one of the following formats:
// - `"COUNTRY_CODE": "READABALE_COUNTRY_NAME"
// - `"COUNTRY_CODE:COUNTRY_REGION": "READABLE_COUNTRY_NAME - READABLE_COUNTRY_REGION"
extension CountryCode {
public extension CountryCode {
var readableCountry: String {
switch self {
// A
Expand Down
80 changes: 80 additions & 0 deletions Modules/Sources/Yosemite/Tools/Settings/SiteAddress.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import Foundation
import WooFoundation

/// Represent and parse the Address of the store, returned in the SiteSettings API `/settings/general/`
///
public class SiteAddress {

private let siteSettings: [SiteSetting]

public var address: String {
return getValueFromSiteSettings(Constants.address) ?? ""
}

public var address2: String {
return getValueFromSiteSettings(Constants.address2) ?? ""
}

public var city: String {
return getValueFromSiteSettings(Constants.city) ?? ""
}

public var postalCode: String {
return getValueFromSiteSettings(Constants.postalCode) ?? ""
}

public var countryCode: CountryCode {
guard let countryComponent = getCountryAndStateComponents().first else {
DDLogError("⛔️ Could not determine country code for site address: no country component found.")
return .unknown
}
guard let countryCode = CountryCode(rawValue: countryComponent) else {
DDLogError("⛔️ Could not determine country code for country: \(countryComponent)")
return .unknown
}
return countryCode
}

/// Returns the name of the country associated with the current store.
/// The default store country is provided in a format like `HK:KOWLOON`
/// This method will transform `HK:KOWLOON` into `Hong Kong`
/// Will return nil if it can not figure out a valid country name
public var countryName: String? {
guard countryCode != .unknown else {
return nil
}

return countryCode.readableCountry
}

public var state: String {
return getCountryAndStateComponents().last ?? ""
}

public init(siteSettings: [SiteSetting]) {
self.siteSettings = siteSettings
}

private func getCountryAndStateComponents() -> [String] {
getValueFromSiteSettings(Constants.countryAndState)?.components(separatedBy: ":") ?? []
}

private func getValueFromSiteSettings(_ settingID: String) -> String? {
return siteSettings.first { (setting) -> Bool in
return setting.settingID == settingID
}?.value
}
}

// MARK: - Constants.
//
private extension SiteAddress {
/// The key of the SiteSetting containing the store address
enum Constants {
static let address = "woocommerce_store_address"
static let address2 = "woocommerce_store_address_2"
static let city = "woocommerce_store_city"
static let postalCode = "woocommerce_store_postcode"
static let countryAndState = "woocommerce_default_country"
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import XCTest
@testable import WooCommerce
@testable import Networking
@testable import Yosemite

final class SiteAddressTests: XCTestCase {

Expand Down
11 changes: 0 additions & 11 deletions WooCommerce/Classes/Extensions/DateFormatter+Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,4 @@ extension DateFormatter {
formatter.setLocalizedDateFormatFromTemplate("hh:mm a")
return formatter
}()

/// Date formatter used for creating a **localized** date and time string.
///
/// Example output in English: "Jan 28, 2018, 11:23 AM"
///
public static let dateAndTimeFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.setLocalizedDateFormatFromTemplate("MMM d yyyy hh:mm a")

return formatter
}()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import class Yosemite.SiteAddress

extension SiteAddress {
convenience init() {
self.init(siteSettings: ServiceLocator.selectedSiteSettings.siteSettings)
Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting, a good way to keep the backwards compatibility and avoid unnecessary changes after the refactoring 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea, there are a few such usage in the app.

}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SwiftUI
import class Yosemite.SiteAddress
import protocol Yosemite.PluginsServiceProtocol
import protocol Yosemite.PointOfSaleSettingsServiceProtocol
import enum Yosemite.Plugin
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SwiftUI
import struct WooFoundation.SafariView

struct PointOfSaleSettingsHardwareDetailView: View {
@Environment(\.dynamicTypeSize) private var dynamicTypeSize
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SwiftUI
import struct WooFoundation.SafariView

struct PointOfSaleSettingsHelpDetailView: View {
@Environment(\.dynamicTypeSize) private var dynamicTypeSize
Expand Down
1 change: 1 addition & 0 deletions WooCommerce/Classes/POS/TabBar/POSIneligibleView.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SwiftUI
import Yosemite

/// A view that displays when the Point of Sale (POS) feature is not available for the current store.
/// Shows the specific reason why POS is ineligible and provides a button to re-check eligibility.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SwiftUI
import struct WooFoundation.SafariSheetView

struct CardReaderManualRowView: View {
// Environment safe areas
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import enum Yosemite.FeatureFlagAction
import enum Yosemite.SettingAction
import protocol Yosemite.PluginsServiceProtocol
import class Yosemite.PluginsService
import class Yosemite.SiteAddress
import class WooFoundation.VersionHelpers

/// Legacy enum containing POS invisible reasons + POSIneligibleReason cases for i1.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import protocol Yosemite.POSSystemStatusServiceProtocol
import class Yosemite.POSSystemStatusService
import protocol Yosemite.POSSiteSettingServiceProtocol
import class Yosemite.POSSiteSettingService
import class Yosemite.SiteAddress
import enum Networking.SiteSettingsFeature
import class WooFoundation.VersionHelpers

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SwiftUI
import struct WooFoundation.SafariSheetView

final class WCShipCTAHostingController: UIHostingController<WCShipCTAView> {

Expand Down
Loading
Loading