Skip to content

Commit 69a5f22

Browse files
committed
Merge branch 'trunk' into woomob-1141-woo-poshistorical-orders-order-details-analytics
2 parents 53d7ded + b5dd3c7 commit 69a5f22

File tree

91 files changed

+2624
-2334
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+2624
-2334
lines changed

.swiftlint.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
swiftlint_version: 0.58.2
22

33
excluded:
4+
- build
45
- BuildTools/.build
56
- DerivedData
67
- fastlane

Modules/Sources/Experiments/DefaultFeatureFlagService.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,6 @@ public struct DefaultFeatureFlagService: FeatureFlagService {
8686
return false
8787
case .productImageOptimizedHandling:
8888
return true
89-
case .pointOfSaleAsATabi2:
90-
return true
9189
case .pointOfSaleOrdersi1:
9290
return true
9391
case .pointOfSaleOrdersi2:

Modules/Sources/Experiments/FeatureFlag.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,6 @@ public enum FeatureFlag: Int {
179179
///
180180
case inventoryProductLabelsInPOS
181181

182-
/// Enables displaying POS as a tab in the tab bar for stores in eligible countries
183-
///
184-
case pointOfSaleAsATabi2
185-
186182
/// Enables displaying Point Of Sale details in order list and order details
187183
///
188184
case pointOfSaleOrdersi1
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import Foundation
2+
3+
/// Tracks the waiting time for a given scenario, allowing to evaluate as analytics
4+
/// how much time in seconds it took between the init and `end` function call
5+
///
6+
public class WaitingTimeTracker {
7+
private let trackScenario: WooAnalyticsEvent.WaitingTime.Scenario
8+
private let currentTimestampSeconds: () -> TimeInterval
9+
private let waitingStartedTimestamp: TimeInterval
10+
11+
public enum TrackingUnit {
12+
case seconds
13+
case milliseconds
14+
}
15+
16+
public init(trackScenario: WooAnalyticsEvent.WaitingTime.Scenario,
17+
currentTimestampSeconds: @escaping () -> TimeInterval = { Date().timeIntervalSince1970 }
18+
) {
19+
self.trackScenario = trackScenario
20+
self.currentTimestampSeconds = currentTimestampSeconds
21+
waitingStartedTimestamp = currentTimestampSeconds()
22+
}
23+
24+
/// Default `end()` method to preserve interface compatibility. By default, tracks in `.seconds`
25+
/// - Returns: The analytics event to be tracked.
26+
///
27+
public func end() -> WooAnalyticsEvent {
28+
end(using: .seconds)
29+
}
30+
31+
/// End the waiting time by evaluating the elapsed time from the init,
32+
/// and returning an analytics event for tracking.
33+
///
34+
/// - Parameter trackingUnit: Defines whether the elapsed time should be tracked in `.seconds` or `.milliseconds` (default is `.seconds`).
35+
/// - Returns: The analytics event to be tracked.
36+
///
37+
public func end(using trackingUnit: TrackingUnit = .seconds) -> WooAnalyticsEvent {
38+
let elapsedTime = calculateElapsedTime(in: trackingUnit)
39+
return .WaitingTime.waitingFinished(scenario: trackScenario, elapsedTime: elapsedTime)
40+
}
41+
42+
/// Calculates elapsed time in the specified tracking unit.
43+
///
44+
private func calculateElapsedTime(in trackingUnit: TrackingUnit) -> TimeInterval {
45+
let elapsedTime = currentTimestampSeconds() - waitingStartedTimestamp
46+
return trackingUnit == .milliseconds ? elapsedTime * 1000 : elapsedTime
47+
}
48+
}
49+
50+
// MARK: - Waiting Time measurement
51+
//
52+
public extension WooAnalyticsEvent {
53+
enum WaitingTime {
54+
/// Possible Waiting time scenarios
55+
public enum Scenario {
56+
case orderDetails
57+
case dashboardTopPerformers
58+
case dashboardMainStats
59+
case analyticsHub
60+
case appStartup
61+
case pointOfSaleLoaded
62+
}
63+
64+
private enum Keys {
65+
static let waitingTime = "waiting_time"
66+
static let millisecondsTimeElapsedInSplashScreen = "milliseconds_time_elapsed_in_splash_screen"
67+
}
68+
69+
static func waitingFinished(scenario: Scenario, elapsedTime: TimeInterval) -> WooAnalyticsEvent {
70+
switch scenario {
71+
case .orderDetails:
72+
return WooAnalyticsEvent(statName: .orderDetailWaitingTimeLoaded, properties: [Keys.waitingTime: elapsedTime])
73+
case .dashboardTopPerformers:
74+
return WooAnalyticsEvent(statName: .dashboardTopPerformersWaitingTimeLoaded, properties: [Keys.waitingTime: elapsedTime])
75+
case .dashboardMainStats:
76+
return WooAnalyticsEvent(statName: .dashboardMainStatsWaitingTimeLoaded, properties: [Keys.waitingTime: elapsedTime])
77+
case .analyticsHub:
78+
return WooAnalyticsEvent(statName: .analyticsHubWaitingTimeLoaded, properties: [Keys.waitingTime: elapsedTime])
79+
case .appStartup:
80+
return WooAnalyticsEvent(statName: .applicationOpenedWaitingTimeLoaded, properties: [Keys.waitingTime: elapsedTime])
81+
case .pointOfSaleLoaded:
82+
return WooAnalyticsEvent(statName: .pointOfSaleLoaded, properties: [Keys.millisecondsTimeElapsedInSplashScreen: elapsedTime])
83+
}
84+
}
85+
}
86+
}

Modules/Sources/Yosemite/Model/Model.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public typealias FallibleCancelable = Hardware.FallibleCancelable
3131
public typealias CommentStatus = Networking.CommentStatus
3232
public typealias CompositeComponentOptionType = Networking.CompositeComponentOptionType
3333
public typealias Coupon = Networking.Coupon
34+
public typealias CouponDiscountType = Networking.Coupon.DiscountType
3435
public typealias CouponReport = Networking.CouponReport
3536
public typealias Country = Networking.Country
3637
public typealias CreateAccountResult = Networking.CreateAccountResult

Modules/Sources/Yosemite/PointOfSale/Coupons/PointOfSaleCouponFetchStrategy.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public protocol PointOfSaleCouponFetchStrategy {
88
func fetchLocalCoupons() async throws -> [POSItem]
99
}
1010

11-
public struct PointOfSaleDefaultCouponFetchStrategy: PointOfSaleCouponFetchStrategy {
11+
struct PointOfSaleDefaultCouponFetchStrategy: PointOfSaleCouponFetchStrategy {
1212
private let siteID: Int64
1313
private let currencySettings: CurrencySettings
1414
private let storage: StorageManagerType
@@ -24,7 +24,7 @@ public struct PointOfSaleDefaultCouponFetchStrategy: PointOfSaleCouponFetchStrat
2424
self.couponStoreMethods = couponStoreMethods
2525
}
2626

27-
public func fetchCoupons(pageNumber: Int) async throws -> PagedItems<POSItem> {
27+
func fetchCoupons(pageNumber: Int) async throws -> PagedItems<POSItem> {
2828
// Update local storage with data from the remote
2929
let hasMorePages = try await syncCouponsFromRemote(pageNumber: pageNumber)
3030
// Return all local coupons, including updated ones from the remote
@@ -35,7 +35,7 @@ public struct PointOfSaleDefaultCouponFetchStrategy: PointOfSaleCouponFetchStrat
3535
/// fetchLocalCoupons provides an array of coupons that are stored locally
3636
/// It does not accept any sort of pagination
3737
/// Limited to default page size to match remote results
38-
public func fetchLocalCoupons() async throws -> [POSItem] {
38+
func fetchLocalCoupons() async throws -> [POSItem] {
3939
return await fetchLocalCoupons(limit: Constants.defaultPageSize)
4040
}
4141
}
@@ -66,7 +66,7 @@ extension PointOfSaleDefaultCouponFetchStrategy {
6666

6767
// MARK: - Search Coupon Strategy
6868

69-
public struct PointOfSaleSearchCouponFetchStrategy: PointOfSaleCouponFetchStrategy {
69+
struct PointOfSaleSearchCouponFetchStrategy: PointOfSaleCouponFetchStrategy {
7070
private let siteID: Int64
7171
private let couponStoreMethods: CouponStoreMethodsProtocol
7272
private let storage: StorageManagerType
@@ -89,7 +89,7 @@ public struct PointOfSaleSearchCouponFetchStrategy: PointOfSaleCouponFetchStrate
8989
self.analytics = analytics
9090
}
9191

92-
public func fetchCoupons(pageNumber: Int) async throws -> PagedItems<POSItem> {
92+
func fetchCoupons(pageNumber: Int) async throws -> PagedItems<POSItem> {
9393
let startTime = Date()
9494
try await couponStoreMethods.searchCoupons(siteID: siteID,
9595
keyword: searchTerm,
@@ -116,7 +116,7 @@ public struct PointOfSaleSearchCouponFetchStrategy: PointOfSaleCouponFetchStrate
116116
return resultsController.fetchCoupons(predicate: predicate)
117117
}
118118

119-
public func fetchLocalCoupons() async throws -> [POSItem] {
119+
func fetchLocalCoupons() async throws -> [POSItem] {
120120
// No support for returning local search results
121121
return []
122122
}

Modules/Sources/Yosemite/PointOfSale/Coupons/PointOfSaleCouponFetchStrategyFactory.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ import class Networking.AlamofireNetwork
66
import struct Combine.AnyPublisher
77
import struct NetworkingCore.JetpackSite
88

9-
public struct PointOfSaleCouponFetchStrategyFactory {
9+
public protocol PointOfSaleCouponFetchStrategyFactoryProtocol {
10+
var defaultStrategy: PointOfSaleCouponFetchStrategy { get }
11+
func searchStrategy(searchTerm: String, analytics: POSItemFetchAnalyticsTracking) -> PointOfSaleCouponFetchStrategy
12+
}
13+
14+
public struct PointOfSaleCouponFetchStrategyFactory: PointOfSaleCouponFetchStrategyFactoryProtocol {
1015
private let siteID: Int64
1116
private let currencySettings: CurrencySettings
1217
private let storage: StorageManagerType
@@ -28,14 +33,14 @@ public struct PointOfSaleCouponFetchStrategyFactory {
2833
self.couponStoreMethods = CouponStoreMethods(storageManager: storage, remote: remote)
2934
}
3035

31-
public var defaultStrategy: PointOfSaleDefaultCouponFetchStrategy {
36+
public var defaultStrategy: PointOfSaleCouponFetchStrategy {
3237
PointOfSaleDefaultCouponFetchStrategy(siteID: siteID,
3338
currencySettings: currencySettings,
3439
storage: storage,
3540
couponStoreMethods: couponStoreMethods)
3641
}
3742

38-
public func searchStrategy(searchTerm: String, analytics: POSItemFetchAnalyticsTracking) -> PointOfSaleSearchCouponFetchStrategy {
43+
public func searchStrategy(searchTerm: String, analytics: POSItemFetchAnalyticsTracking) -> PointOfSaleCouponFetchStrategy {
3944
PointOfSaleSearchCouponFetchStrategy(siteID: siteID,
4045
currencySettings: currencySettings,
4146
storage: storage,

Modules/Sources/Yosemite/PointOfSale/Items/PointOfSaleItemFetchStrategyFactory.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ public protocol PointOfSaleItemFetchStrategyFactoryProtocol {
1010

1111
func searchStrategy(searchTerm: String,
1212
analytics: POSItemFetchAnalyticsTracking) -> PointOfSalePurchasableItemFetchStrategy
13-
14-
func popularStrategy(pageSize: Int) -> PointOfSalePurchasableItemFetchStrategy
1513
}
1614

1715
public final class PointOfSaleItemFetchStrategyFactory: PointOfSaleItemFetchStrategyFactoryProtocol {
@@ -69,8 +67,4 @@ public final class PointOfSaleFixedItemFetchStrategyFactory: PointOfSaleItemFetc
6967
analytics: POSItemFetchAnalyticsTracking) -> PointOfSalePurchasableItemFetchStrategy {
7068
fixedStrategy
7169
}
72-
73-
public func popularStrategy(pageSize: Int) -> PointOfSalePurchasableItemFetchStrategy {
74-
fixedStrategy
75-
}
7670
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import Foundation
2+
import GRDB
3+
import protocol Storage.GRDBManagerProtocol
4+
5+
public protocol POSCatalogSettingsServiceProtocol {
6+
/// Gets catalog information for the specified site.
7+
/// - Parameter siteID: The site ID to get catalog information for.
8+
/// - Returns: Catalog information including statistics and sync dates.
9+
func loadCatalogInfo(for siteID: Int64) async throws -> POSCatalogInfo
10+
}
11+
12+
public struct POSCatalogInfo {
13+
public let productCount: Int
14+
public let variationCount: Int
15+
public let lastFullSyncDate: Date?
16+
public let lastIncrementalSyncDate: Date?
17+
18+
public init(productCount: Int, variationCount: Int, lastFullSyncDate: Date?, lastIncrementalSyncDate: Date?) {
19+
self.productCount = productCount
20+
self.variationCount = variationCount
21+
self.lastFullSyncDate = lastFullSyncDate
22+
self.lastIncrementalSyncDate = lastIncrementalSyncDate
23+
}
24+
}
25+
26+
public class POSCatalogSettingsService: POSCatalogSettingsServiceProtocol {
27+
private let grdbManager: GRDBManagerProtocol
28+
29+
public init(grdbManager: GRDBManagerProtocol) {
30+
self.grdbManager = grdbManager
31+
}
32+
33+
public func loadCatalogInfo(for siteID: Int64) async throws -> POSCatalogInfo {
34+
try await grdbManager.databaseConnection.read { db in
35+
let productCount = try PersistedProduct.filter { $0.siteID == siteID }.fetchCount(db)
36+
let variationCount = try PersistedProductVariation.filter { $0.siteID == siteID }.fetchCount(db)
37+
let site = try PersistedSite.filter(key: siteID).fetchOne(db)
38+
return POSCatalogInfo(
39+
productCount: productCount,
40+
variationCount: variationCount,
41+
lastFullSyncDate: site?.lastCatalogFullSyncDate,
42+
lastIncrementalSyncDate: site?.lastCatalogIncrementalSyncDate
43+
)
44+
}
45+
}
46+
}

Modules/Sources/Yosemite/Tools/Plugins/PluginsService.swift

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,8 @@
11
import Foundation
2-
import WooFoundation
32
import protocol Storage.StorageManagerType
43

54
/// A service for system plugins.
65
public protocol PluginsServiceProtocol {
7-
/// Waits for a specific plugin to be available in storage.
8-
/// - Parameters:
9-
/// - siteID: The site ID to search for the plugin.
10-
/// - pluginPath: The plugin's file path (e.g., "woocommerce/woocommerce.php" for WooCommerce).
11-
/// - isActive: Whether the plugin is active or not.
12-
/// - Returns: The SystemPlugin when found in storage.
13-
func waitForPluginInStorage(siteID: Int64, pluginPath: String, isActive: Bool) async -> SystemPlugin
14-
156
/// Loads a specific plugin from storage synchronously.
167
/// - Parameters:
178
/// - siteID: The site ID to search for the plugin.
@@ -37,36 +28,6 @@ public class PluginsService: PluginsServiceProtocol {
3728
self.storageManager = storageManager
3829
}
3930

40-
@MainActor
41-
public func waitForPluginInStorage(siteID: Int64, pluginPath: String, isActive: Bool) async -> SystemPlugin {
42-
let predicate = \StorageSystemPlugin.siteID == siteID && \StorageSystemPlugin.plugin == pluginPath && \StorageSystemPlugin.active == isActive
43-
let pluginDescriptor = NSSortDescriptor(keyPath: \StorageSystemPlugin.plugin, ascending: true)
44-
let resultsController = ResultsController<StorageSystemPlugin>(storageManager: storageManager,
45-
matching: predicate,
46-
fetchLimit: 1,
47-
sortedBy: [pluginDescriptor])
48-
do {
49-
try resultsController.performFetch()
50-
if let plugin = resultsController.fetchedObjects.first {
51-
return plugin
52-
}
53-
} catch {
54-
DDLogError("Error loading plugin \(pluginPath) for site \(siteID) initially: \(error.localizedDescription)")
55-
}
56-
57-
return await withCheckedContinuation { continuation in
58-
var hasResumed = false
59-
resultsController.onDidChangeContent = {
60-
guard let plugin = resultsController.fetchedObjects.first,
61-
!hasResumed else {
62-
return
63-
}
64-
hasResumed = true
65-
continuation.resume(returning: plugin)
66-
}
67-
}
68-
}
69-
7031
@MainActor
7132
public func loadPluginInStorage(siteID: Int64, plugin: Plugin, isActive: Bool?) -> SystemPlugin? {
7233
storageManager.viewStorage.loadSystemPlugin(siteID: siteID,

0 commit comments

Comments
 (0)