Skip to content
Closed
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
67 changes: 67 additions & 0 deletions Modules/.swiftpm/xcode/xcshareddata/xcschemes/PointOfSale.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2600"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "PointOfSale"
BuildableName = "PointOfSale"
BlueprintName = "PointOfSale"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "PointOfSale"
BuildableName = "PointOfSale"
BlueprintName = "PointOfSale"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
24 changes: 23 additions & 1 deletion Modules/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ let package = Package(
name: "Yosemite",
targets: ["Yosemite"]
),
.library(
name: "PointOfSale",
targets: ["PointOfSale"]
),
],
dependencies: [
.package(url: "https://github.com/Alamofire/Alamofire", from: "5.2.0"),
Expand Down Expand Up @@ -230,6 +234,17 @@ let package = Package(
.product(name: "WordPressEditor", package: "AztecEditor-iOS"),
]
),
.target(
name: "PointOfSale",
dependencies: [
"Experiments",
"WooFoundation",
"Yosemite",
.product(name: "CocoaLumberjackSwift", package: "CocoaLumberjack"),
.product(name: "Shimmer", package: "SwiftUI-Shimmer"),
.product(name: "Kingfisher", package: "Kingfisher"),
]
),
.testTarget(
name: "ExperimentsTests",
dependencies: [
Expand Down Expand Up @@ -295,7 +310,13 @@ let package = Package(
.process("Resources"),
.process("../NetworkingTests/Responses")
]
)
),
.testTarget(
name: "PointOfSaleTests",
dependencies: [
"PointOfSale"
]
),
]
)

Expand Down Expand Up @@ -374,6 +395,7 @@ enum XcodeSupport {
"WordPressUI",
"WPMediaPicker",
"Yosemite",
"PointOfSale",
.product(name: "Alamofire", package: "Alamofire"),
.product(name: "Algorithms", package: "swift-algorithms"),
.product(name: "AutomatticAbout", package: "AutomatticAbout-swift"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import struct Yosemite.POSSimpleProduct
import struct Yosemite.POSVariation
import enum WooFoundation.CountryCode
import enum Yosemite.PaymentMethod
import struct WooFoundation.WooAnalyticsEvent

extension WooAnalyticsEvent {
enum PointOfSale {
Expand Down Expand Up @@ -125,7 +126,7 @@ extension WooAnalyticsEvent {
Key.cardReaderModel: readerModel(for: cardReaderModel),
Key.countryCode: countryCode.rawValue,
Key.gatewayID: safeGatewayID(for: forGatewayID),
Key.paymentMethodType: paymentMethod.analyticsValue,
Key.paymentMethodType: analyticsValue(for: paymentMethod),
Key.millisecondsSinceCustomerInteractionStarted: "\(millisecondsSinceCustomerIteractionStarted)",
Key.millisecondsSinceOrderSyncSuccess: "\(millisecondsSinceOrderSyncSuccess)",
Key.millisecondsSinceReaderReadyToCollect: "\(millisecondsSinceReaderReadyToCollect)",
Expand All @@ -134,6 +135,17 @@ extension WooAnalyticsEvent {
])
}

static func analyticsValue(for paymentMethod: PaymentMethod) -> String {
switch paymentMethod {
case .card, .cardPresent:
return "card"
case .interacPresent:
return "card_interac"
case .unknown:
return "unknown"
}
}

static func cashCollectPaymentSuccess(millisecondsSinceCustomerIteractionStarted: Double) -> WooAnalyticsEvent {
WooAnalyticsEvent(statName: .pointOfSaleCashCollectPaymentSuccess, properties: [
Key.millisecondsSinceCustomerInteractionStarted: "\(millisecondsSinceCustomerIteractionStarted)",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import struct WooFoundation.WooAnalyticsEvent

extension WooAnalyticsEvent {
enum PointOfSaleIneligibleUI {
/// Event property key.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
import SwiftUI
import protocol Experiments.FeatureFlagService

public protocol POSEntryPointEligibilityCheckerProtocol {
/// Checks the initial visibility of the POS tab.
func checkInitialVisibility() -> Bool
/// Checks the final visibility of the POS tab.
func checkVisibility() async -> Bool
/// Determines whether the site is eligible for POS.
func checkEligibility() async -> POSEligibilityState
/// Refreshes the eligibility state based on the provided ineligible reason.
func refreshEligibility(ineligibleReason: POSIneligibleReason) async throws -> POSEligibilityState
}

@Observable final class POSEntryPointController {
private(set) var eligibilityState: POSEligibilityState?
private let posEligibilityChecker: POSEntryPointEligibilityCheckerProtocol
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import struct Yosemite.POSOrder
import struct Yosemite.POSOrderItem
import struct Yosemite.POSOrderRefund
import class Yosemite.Store
import class Yosemite.AsyncPaginationTracker

protocol POSOrderListControllerProtocol {
var ordersViewState: POSOrderListState { get }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import protocol Yosemite.PointOfSaleItemServiceProtocol
import protocol Yosemite.PointOfSaleCouponServiceProtocol
import struct Yosemite.PointOfSaleCouponFetchStrategyFactory
import protocol Yosemite.PointOfSaleCouponFetchStrategy
import class Yosemite.AsyncPaginationTracker

protocol PointOfSaleCouponsControllerProtocol: PointOfSaleSearchingItemsControllerProtocol {
/// Enables coupons in store settings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import enum Yosemite.PointOfSaleItemServiceError
import struct Yosemite.POSVariableParentProduct
import class Yosemite.Store
import enum Yosemite.POSItemType
import class Yosemite.AsyncPaginationTracker

protocol PointOfSaleItemsControllerProtocol {
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import class WooFoundation.VersionHelpers
import protocol Yosemite.POSOrderServiceProtocol
import protocol Yosemite.POSReceiptServiceProtocol
import protocol Yosemite.PluginsServiceProtocol
import protocol Yosemite.PaymentCaptureCelebrationProtocol
import class Yosemite.PaymentCaptureCelebration
import struct Yosemite.Order
import struct Yosemite.POSCart
import struct Yosemite.POSCartItem
Expand All @@ -20,6 +22,8 @@ import enum WooFoundation.CurrencyCode
import protocol WooFoundation.Analytics
import enum Alamofire.AFError
import class Yosemite.OrderTotalsCalculator
import struct WooFoundation.WooAnalyticsEvent
import protocol WooFoundationCore.WooAnalyticsEventPropertyType

enum SyncOrderState {
case newOrder
Expand Down Expand Up @@ -281,3 +285,34 @@ private extension POSCart {
self.init(items: items, coupons: coupons)
}
}

private extension WooAnalyticsEvent {
struct Orders {
// MARK: - Order Creation Events

/// Matches errors on Android for consistency
/// Only coupon tracking is relevant for now
enum OrderCreationErrorType: String {
case invalidCoupon = "INVALID_COUPON"
}

static func orderCreationFailed(
usesGiftCard: Bool,
errorContext: String,
errorDescription: String,
errorType: OrderCreationErrorType? = nil
) -> WooAnalyticsEvent {
var properties: [String: WooAnalyticsEventPropertyType] = [
"use_gift_card": usesGiftCard,
"error_context": errorContext,
"error_description": errorDescription
]

if let errorType {
properties["error_type"] = errorType.rawValue
}

return WooAnalyticsEvent(statName: .orderCreationFailed, properties: properties)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import enum WooFoundation.CountryCode
import enum WooFoundation.CurrencyCode

/// Represents the reasons why a site may be ineligible for POS.
enum POSIneligibleReason: Equatable {
public enum POSIneligibleReason: Equatable {
case unsupportedIOSVersion
case unsupportedWooCommerceVersion(minimumVersion: String)
case siteSettingsNotAvailable
Expand All @@ -14,7 +14,7 @@ enum POSIneligibleReason: Equatable {
}

/// Represents the eligibility state for POS.
enum POSEligibilityState: Equatable {
public enum POSEligibilityState: Equatable {
case eligible
case ineligible(reason: POSIneligibleReason)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import Foundation
import enum Yosemite.CollectOrderPaymentUseCaseError
import enum Yosemite.CollectOrderPaymentUseCaseNotValidAmountError

enum PointOfSaleCardPresentPaymentEventPresentationStyle {
case message(PointOfSaleCardPresentPaymentMessageType)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation
import enum Networking.DotcomError
import enum Yosemite.CollectOrderPaymentUseCaseNotValidAmountError

struct PointOfSaleCardPresentPaymentValidatingOrderErrorMessageViewModel: Equatable {
let title: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import SwiftUI
struct PointOfSaleCardPresentPaymentConnectingFailedUpdateAddressView: View {
@StateObject var viewModel: PointOfSaleCardPresentPaymentConnectingFailedUpdateAddressAlertViewModel
let animation: POSCardPresentPaymentAlertAnimation
@Environment(\.posExternalViews) private var externalViews

var body: some View {
VStack(spacing: PointOfSaleReaderConnectionModalLayout.contentButtonSpacing) {
Expand All @@ -29,8 +30,8 @@ struct PointOfSaleCardPresentPaymentConnectingFailedUpdateAddressView: View {
.multilineTextAlignment(.center)
.accessibilityElement(children: .contain)
.posSheet(isPresented: $viewModel.shouldShowSettingsWebView) {
WCSettingsWebView(adminUrl: viewModel.settingsAdminUrl,
completion: viewModel.settingsWebViewWasDismissed)
externalViews.createWCWebView(adminUrl: viewModel.settingsAdminUrl,
completion: viewModel.settingsWebViewWasDismissed)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation
import enum Yosemite.POSItemType
import struct WooFoundation.WooAnalyticsEvent

struct PointOfSaleItemListAnalyticsTracker {
private let sourceView: WooAnalyticsEvent.PointOfSale.SourceView
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ struct PointOfSaleEntryPointView: View {
searchHistoryService: PointOfSalePreviewHistoryService(),
popularPurchasableItemsController: PointOfSalePreviewItemsController(),
barcodeScanService: PointOfSalePreviewBarcodeScanService(),
posEligibilityChecker: POSTabEligibilityChecker(siteID: 0),
posEligibilityChecker: PointOfSalePreviewTabEligibilityChecker(),
services: POSPreviewServices())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,9 @@ struct PointOfSaleSettingsHardwareDetailView: View {
}
.navigationBarBackButtonHidden(true)
.posFullScreenCover(isPresented: $showCardReaderDocumentationModal) {
SafariView(url: WooConstants.URLs.inPersonPaymentsLearnMoreWCPay.asURL())
if let url = URL(string: Constants.inPersonPaymentsLearnMoreWCPay.rawValue) {
SafariView(url: url)
}
}
}

Expand Down Expand Up @@ -383,6 +385,13 @@ private extension PointOfSaleSettingsHardwareDetailView {
}
}

private extension PointOfSaleSettingsHardwareDetailView {
enum Constants: String {
case inPersonPaymentsLearnMoreWCPay =
"https://woocommerce.com/document/woocommerce-payments/in-person-payments/getting-started-with-in-person-payments/"
}
}

#if DEBUG
#Preview {
PointOfSaleSettingsHardwareDetailView(settingsController: PointOfSaleSettingsPreviewController())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ protocol POSExternalViewProviding {
title: String,
cancelButtonTitle: String,
onSelection: @escaping (Coupon.DiscountType) -> Void) -> AnyView
func createWCWebView(adminUrl: URL, completion: @escaping () -> Void) -> AnyView
}

/// Main protocol that combines all POS dependency providers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,5 +142,8 @@ struct EmptyPOSExternalView: POSExternalViewProviding {
onSelection: @escaping (Coupon.DiscountType) -> Void) -> AnyView {
AnyView(EmptyView())
}
func createWCWebView(adminUrl: URL, completion: @escaping () -> Void) -> AnyView {
AnyView(EmptyView())
}
init() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,13 @@ final class PointOfSalePreviewBarcodeScanService: PointOfSaleBarcodeScanServiceP
}
}

final class PointOfSalePreviewTabEligibilityChecker: POSEntryPointEligibilityCheckerProtocol {
func checkInitialVisibility() -> Bool { true }
func checkVisibility() async -> Bool { true }
func checkEligibility() async -> POSEligibilityState { .eligible }
func refreshEligibility(ineligibleReason: POSIneligibleReason) async throws -> POSEligibilityState { .eligible }
}

final class POSReceiptSenderPreview: POSReceiptSending {
func sendReceipt(orderID: Int64, recipientEmail: String) async throws {}
}
Expand Down
Loading