diff --git a/Modules/Sources/Experiments/DefaultFeatureFlagService.swift b/Modules/Sources/Experiments/DefaultFeatureFlagService.swift index eb6df981c6e..9899b5c9b76 100644 --- a/Modules/Sources/Experiments/DefaultFeatureFlagService.swift +++ b/Modules/Sources/Experiments/DefaultFeatureFlagService.swift @@ -96,7 +96,7 @@ public struct DefaultFeatureFlagService: FeatureFlagService { case .pointOfSaleAsATabi1: return true case .pointOfSaleAsATabi2: - return buildConfig == .localDeveloper || buildConfig == .alpha + return true case .pointOfSaleOrdersi1: return true case .pointOfSaleOrdersi2: diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 1dcc4b28f0c..5dcaa89ccf0 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -7,6 +7,7 @@ - [*] Shipping Labels: Improved VoiceOver accessibility [https://github.com/woocommerce/woocommerce-ios/pull/15912] - [**] Order Details: Update Shipping Labels section for stores with Woo Shipping extension [https://github.com/woocommerce/woocommerce-ios/pull/15889] - [*] Order List: New orders made through Point of Sale are now filterable via the Order List filters menu [https://github.com/woocommerce/woocommerce-ios/pull/15910] +- [*] POS: a POS tab in the tab bar is now available in the app for stores in countries eligible for Point of Sale, instead of the tab is only shown when the store is eligible for POS. [https://github.com/woocommerce/woocommerce-ios/pull/15918] - [*] Shipping Labels: Display base rate on selected shipping service cards [https://github.com/woocommerce/woocommerce-ios/pull/15916] - [*] Shipping Labels: Update mark order completed toggle on purchase form [https://github.com/woocommerce/woocommerce-ios/pull/15917] - [internal] Optimized assets for app size reduction [https://github.com/woocommerce/woocommerce-ios/pull/15881] @@ -795,7 +796,7 @@ - [Internal] New default property `plan` is tracked in every event for logged-in users. [https://github.com/woocommerce/woocommerce-ios/pull/10356] - [Internal] Google sign in now defaults to bypassing the Google SDK [https://github.com/woocommerce/woocommerce-ios/pull/10341] - [*] Product list filter (Products tab and order creation > add products > filter): product types from extensions supported in the app are now available for product filtering - subscription, variable subscription, bundle, and composite. [https://github.com/woocommerce/woocommerce-ios/pull/10382] - + 14.7 ----- - [*] Local notifications: Add a reminder to purchase a plan is scheduled 6hr after a free trial subscription. [https://github.com/woocommerce/woocommerce-ios/pull/10268] diff --git a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift index 8be1792ae0a..46e2892c54f 100644 --- a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift +++ b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift @@ -1272,6 +1272,8 @@ enum WooAnalyticsStat: String { // MARK: Point of Sale events case pointOfSaleTabSelected = "main_tab_pos_selected" case pointOfSaleTabVisibilityChecked = "pos_tab_visibility_checked" + case pointOfSaleIneligibleUIShown = "pos_ineligible_ui_shown" + case pointOfSaleIneligibleUIRetryTapped = "pos_ineligible_ui_retry_tapped" case pointOfSaleLoaded = "loaded" case pointOfSaleItemsFetched = "items_fetched" case pointOfSaleItemsPullToRefresh = "items_pull_to_refresh" diff --git a/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSaleIneligibleUI.swift b/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSaleIneligibleUI.swift new file mode 100644 index 00000000000..94eb13a2200 --- /dev/null +++ b/WooCommerce/Classes/POS/Analytics/WooAnalyticsEvent+PointOfSaleIneligibleUI.swift @@ -0,0 +1,36 @@ +extension WooAnalyticsEvent { + enum PointOfSaleIneligibleUI { + /// Event property key. + private enum Key { + static let reason = "reason" + } + + static func ineligibleUIShown(reason: POSIneligibleReason) -> WooAnalyticsEvent { + WooAnalyticsEvent(statName: .pointOfSaleIneligibleUIShown, properties: [Key.reason: reason.analyticsValue]) + } + + static func ineligibleUIRetryTapped(reason: POSIneligibleReason) -> WooAnalyticsEvent { + WooAnalyticsEvent(statName: .pointOfSaleIneligibleUIRetryTapped, properties: [Key.reason: reason.analyticsValue]) + } + } +} + +private extension POSIneligibleReason { + var analyticsValue: String { + switch self { + case .unsupportedCurrency: + return "store_currency" + case .unsupportedWooCommerceVersion: + return "wc_plugin_version" + case .featureSwitchDisabled: + return "feature_switch_disabled" + case .wooCommercePluginNotFound: + return "unknown_wc_plugin" + case .unsupportedIOSVersion: + return "ios_version" + case .siteSettingsNotAvailable, + .selfDeallocated: + return "other" + } + } +} diff --git a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleLoadingView.swift b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleLoadingView.swift index d96510217f8..97059e5d01d 100644 --- a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleLoadingView.swift +++ b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleLoadingView.swift @@ -1,8 +1,6 @@ import SwiftUI struct PointOfSaleLoadingView: View { - @State private var waitingTimeTracker: WaitingTimeTracker? - var body: some View { HStack(alignment: .center) { Spacer() @@ -15,28 +13,10 @@ struct PointOfSaleLoadingView: View { .multilineTextAlignment(.center) Spacer() } - .onAppear { - trackTimeOnAppear() - } - .onDisappear { - trackElapsedTimeOnDisappear() - } .background(Color.posSurface) } } -private extension PointOfSaleLoadingView { - func trackTimeOnAppear() { - waitingTimeTracker = WaitingTimeTracker(trackScenario: .pointOfSaleLoaded) - } - - func trackElapsedTimeOnDisappear() { - if let waitingTimeTracker = waitingTimeTracker { - waitingTimeTracker.end(using: .milliseconds) - } - } -} - #Preview { PointOfSaleLoadingView() } diff --git a/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift b/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift index 8d253b47af3..9d5f92f0b8c 100644 --- a/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift +++ b/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift @@ -8,6 +8,7 @@ struct PointOfSaleDashboardView: View { @State private var showExitPOSModal: Bool = false @State private var showSupport: Bool = false @State private var showDocumentation: Bool = false + @State private var waitingTimeTracker: WaitingTimeTracker? @State private var floatingSize: CGSize = .zero @@ -138,6 +139,14 @@ struct PointOfSaleDashboardView: View { } } .ignoresSafeArea(.keyboard) + .onAppear { + trackTimeForInitialLoadingState() + } + .onChange(of: viewState) { oldValue, newValue in + if newValue == .content && oldValue != newValue { + trackElapsedTimeForInitialLoadingState() + } + } } private var contentView: some View { @@ -210,6 +219,20 @@ private extension PointOfSaleDashboardView { } } +@available(iOS 17.0, *) +private extension PointOfSaleDashboardView { + func trackTimeForInitialLoadingState() { + waitingTimeTracker = WaitingTimeTracker(trackScenario: .pointOfSaleLoaded) + } + + func trackElapsedTimeForInitialLoadingState() { + if let waitingTimeTracker { + waitingTimeTracker.end(using: .milliseconds) + self.waitingTimeTracker = nil + } + } +} + struct FloatingControlAreaSizeKey: EnvironmentKey { static let defaultValue = CGSize.zero } diff --git a/WooCommerce/Classes/POS/TabBar/POSIneligibleView.swift b/WooCommerce/Classes/POS/TabBar/POSIneligibleView.swift index 0f59d4aab96..eaff4267021 100644 --- a/WooCommerce/Classes/POS/TabBar/POSIneligibleView.swift +++ b/WooCommerce/Classes/POS/TabBar/POSIneligibleView.swift @@ -42,6 +42,9 @@ struct POSIneligibleView: View { Task { @MainActor in do { isLoading = true + ServiceLocator.analytics.track( + event: .PointOfSaleIneligibleUI.ineligibleUIRetryTapped(reason: reason) + ) try await onRefresh() isLoading = false } catch { @@ -71,6 +74,12 @@ struct POSIneligibleView: View { Spacer() } .padding(POSPadding.large) + .onAppear { + ServiceLocator.analytics.track(event: .PointOfSaleIneligibleUI.ineligibleUIShown(reason: reason)) + } + .onChange(of: reason) { newReason in + ServiceLocator.analytics.track(event: .PointOfSaleIneligibleUI.ineligibleUIShown(reason: newReason)) + } } private var suggestionText: String { diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 376b359b5b7..0e884841456 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -168,6 +168,7 @@ 02162727237963AF000208D2 /* ProductFormViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 02162725237963AF000208D2 /* ProductFormViewController.xib */; }; 02162729237965E8000208D2 /* ProductFormTableViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02162728237965E8000208D2 /* ProductFormTableViewModel.swift */; }; 0216272B2379662C000208D2 /* DefaultProductFormTableViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0216272A2379662C000208D2 /* DefaultProductFormTableViewModel.swift */; }; + 0216DA702E2576CB00016600 /* WooAnalyticsEvent+PointOfSaleIneligibleUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0216DA6F2E2576C300016600 /* WooAnalyticsEvent+PointOfSaleIneligibleUI.swift */; }; 0218B4EC242E06F00083A847 /* MediaType+WPMediaType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0218B4EB242E06F00083A847 /* MediaType+WPMediaType.swift */; }; 0219B03723964527007DCD5E /* PaginatedProductShippingClassListSelectorDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0219B03623964527007DCD5E /* PaginatedProductShippingClassListSelectorDataSource.swift */; }; 021A17212D7036AF006DF7C0 /* DynamicFrameScaler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 021A17202D7036AF006DF7C0 /* DynamicFrameScaler.swift */; }; @@ -3345,6 +3346,7 @@ 02162725237963AF000208D2 /* ProductFormViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ProductFormViewController.xib; sourceTree = ""; }; 02162728237965E8000208D2 /* ProductFormTableViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductFormTableViewModel.swift; sourceTree = ""; }; 0216272A2379662C000208D2 /* DefaultProductFormTableViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultProductFormTableViewModel.swift; sourceTree = ""; }; + 0216DA6F2E2576C300016600 /* WooAnalyticsEvent+PointOfSaleIneligibleUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WooAnalyticsEvent+PointOfSaleIneligibleUI.swift"; sourceTree = ""; }; 0218B4EB242E06F00083A847 /* MediaType+WPMediaType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MediaType+WPMediaType.swift"; sourceTree = ""; }; 0219B03623964527007DCD5E /* PaginatedProductShippingClassListSelectorDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginatedProductShippingClassListSelectorDataSource.swift; sourceTree = ""; }; 021A17202D7036AF006DF7C0 /* DynamicFrameScaler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicFrameScaler.swift; sourceTree = ""; }; @@ -7735,6 +7737,7 @@ 02D1D2D82CD3CD710069A93F /* Analytics */ = { isa = PBXGroup; children = ( + 0216DA6F2E2576C300016600 /* WooAnalyticsEvent+PointOfSaleIneligibleUI.swift */, 68F68A4F2D6730DF00BB9568 /* POSCollectOrderPaymentAnalyticsTracking.swift */, 68F896412D5E4321000B308B /* POSCollectOrderPaymentAnalytics.swift */, 02D1D2D92CD3CD8D0069A93F /* WooAnalyticsEvent+PointOfSale.swift */, @@ -16664,6 +16667,7 @@ B59D1EE5219080B4009D1978 /* Note+Woo.swift in Sources */, 02913E9523A774C500707A0C /* UnitInputFormatter.swift in Sources */, 0204E3622B8CD40B00F1B5FD /* WooAnalyticsEvent+Products.swift in Sources */, + 0216DA702E2576CB00016600 /* WooAnalyticsEvent+PointOfSaleIneligibleUI.swift in Sources */, 4508BF622660E34A00707869 /* ShippingLabelCarrierAndRatesTopBanner.swift in Sources */, DE7E5E7F2B4BC52C002E28D2 /* MultiSelectionList.swift in Sources */, DA0DBE2F2C4FC61D00DF14C0 /* POSFloatingControlView.swift in Sources */,