diff --git a/WooCommerce/Resources/Info.plist b/WooCommerce/Resources/Info.plist
index 5dc456bb3ad..0313da7264e 100644
--- a/WooCommerce/Resources/Info.plist
+++ b/WooCommerce/Resources/Info.plist
@@ -76,7 +76,9 @@
NSPhotoLibraryUsageDescription
To save photos from camera for Product images, or to add photos or videos to your Products or support tickets.
NSUserActivityTypes
-
+
+ StoreWidgetsConfigIntent
+
UIAppFonts
Noticons.ttf
diff --git a/WooCommerce/StoreWidgets/StatsTimeRange.swift b/WooCommerce/StoreWidgets/StatsTimeRange.swift
new file mode 100644
index 00000000000..20346eeab58
--- /dev/null
+++ b/WooCommerce/StoreWidgets/StatsTimeRange.swift
@@ -0,0 +1,28 @@
+/// Represents the time range for an Order Stats v4 model.
+/// This is a local property and not in the remote response.
+///
+/// - today: hourly data starting midnight today until now.
+/// - thisWeek: daily data starting Sunday of this week until now.
+/// - thisMonth: daily data starting 1st of this month until now.
+/// - thisYear: monthly data starting January of this year until now.
+enum StatsTimeRange: String {
+ case today
+ case thisWeek
+ case thisMonth
+ case thisYear
+}
+
+extension StatsTimeRange {
+ init(_ timeRange: IntentTimeRange) {
+ switch timeRange {
+ case .unknown, .today:
+ self = .today
+ case .thisWeek:
+ self = .thisWeek
+ case .thisMonth:
+ self = .thisMonth
+ case .thisYear:
+ self = .thisYear
+ }
+ }
+}
diff --git a/WooCommerce/StoreWidgets/StoreInfoDataService.swift b/WooCommerce/StoreWidgets/StoreInfoDataService.swift
index b5aa4af8828..c9f13df7515 100644
--- a/WooCommerce/StoreWidgets/StoreInfoDataService.swift
+++ b/WooCommerce/StoreWidgets/StoreInfoDataService.swift
@@ -17,7 +17,7 @@ final class StoreInfoDataService {
///
private var orderStatsRemoteV4: OrderStatsRemoteV4
- /// Visitors remoute source
+ /// Visitors remote source
///
private var siteVisitStatsRemote: SiteVisitStatsRemote
@@ -31,9 +31,9 @@ final class StoreInfoDataService {
siteVisitStatsRemote = SiteVisitStatsRemote(network: network)
}
- /// Async function that fetches todays stats data.
+ /// Async function that fetches stats data for given time range.
///
- func fetchTodayStats(for storeID: Int64) async throws -> Stats {
+ func fetchStats(for storeID: Int64, timeRange: StatsTimeRange) async throws -> Stats {
// Prepare them to run in parallel
async let revenueAndOrdersRequest = fetchTodaysRevenueAndOrders(for: storeID)
async let visitorsRequest = fetchTodaysVisitors(for: storeID)
diff --git a/WooCommerce/StoreWidgets/StoreInfoProvider.swift b/WooCommerce/StoreWidgets/StoreInfoProvider.swift
index a20568efea8..5a85ad9b09b 100644
--- a/WooCommerce/StoreWidgets/StoreInfoProvider.swift
+++ b/WooCommerce/StoreWidgets/StoreInfoProvider.swift
@@ -56,7 +56,7 @@ struct StoreInfoData {
/// Type that provides data entries to the widget system.
///
-final class StoreInfoProvider: TimelineProvider {
+final class StoreInfoProvider: IntentTimelineProvider {
/// Holds a reference to the service while a network request is being performed.
///
@@ -75,13 +75,13 @@ final class StoreInfoProvider: TimelineProvider {
/// Quick Snapshot. Required when previewing the widget.
///
- func getSnapshot(in context: Context, completion: @escaping (StoreInfoEntry) -> Void) {
+ func getSnapshot(for configuration: StoreWidgetsConfigIntent, in context: Context, completion: @escaping (StoreInfoEntry) -> Void) {
completion(placeholder(in: context))
}
/// Real data widget.
///
- func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) {
+ func getTimeline(for configuration: StoreWidgetsConfigIntent, in context: Context, completion: @escaping (Timeline) -> Void) {
guard let dependencies = Self.fetchDependencies() else {
return completion(Timeline(entries: [StoreInfoEntry.notConnected], policy: .never))
}
@@ -90,7 +90,7 @@ final class StoreInfoProvider: TimelineProvider {
networkService = strongService
Task {
do {
- let todayStats = try await strongService.fetchTodayStats(for: dependencies.storeID)
+ let todayStats = try await strongService.fetchStats(for: dependencies.storeID, timeRange: StatsTimeRange(configuration.timeRange))
let entry = Self.dataEntry(for: todayStats, with: dependencies)
let reloadDate = Date(timeIntervalSinceNow: reloadInterval)
let timeline = Timeline(entries: [entry], policy: .after(reloadDate))
diff --git a/WooCommerce/StoreWidgets/StoreInfoWidget.swift b/WooCommerce/StoreWidgets/StoreInfoWidget.swift
index 31ff2c1bcee..4c945557e92 100644
--- a/WooCommerce/StoreWidgets/StoreInfoWidget.swift
+++ b/WooCommerce/StoreWidgets/StoreInfoWidget.swift
@@ -18,7 +18,7 @@ struct StoreInfoWidget: Widget {
}
var body: some WidgetConfiguration {
- StaticConfiguration(kind: WooConstants.storeInfoWidgetKind, provider: StoreInfoProvider()) { entry in
+ IntentConfiguration(kind: WooConstants.storeInfoWidgetKind, intent: StoreWidgetsConfigIntent.self, provider: StoreInfoProvider()) { entry in
StoreInfoWidgetEntryView(entry: entry)
}
.configurationDisplayName(Localization.title)
diff --git a/WooCommerce/StoreWidgets/StoreWidgets.intentdefinition b/WooCommerce/StoreWidgets/StoreWidgets.intentdefinition
index 5aaeb8d2ba7..d2c386e8e88 100644
--- a/WooCommerce/StoreWidgets/StoreWidgets.intentdefinition
+++ b/WooCommerce/StoreWidgets/StoreWidgets.intentdefinition
@@ -3,19 +3,185 @@
INEnums
-
+
+
+ INEnumDisplayName
+ Time Range
+ INEnumDisplayNameID
+ pG2KCr
+ INEnumGeneratesHeader
+
+ INEnumName
+ IntentTimeRange
+ INEnumType
+ Regular
+ INEnumValues
+
+
+ INEnumValueDisplayName
+ unknown
+ INEnumValueDisplayNameID
+ whdG6s
+ INEnumValueName
+ unknown
+
+
+ INEnumValueDisplayName
+ Today
+ INEnumValueDisplayNameID
+ Jj2AK7
+ INEnumValueIndex
+ 1
+ INEnumValueName
+ today
+
+
+ INEnumValueDisplayName
+ This Week
+ INEnumValueDisplayNameID
+ jzdldh
+ INEnumValueIndex
+ 2
+ INEnumValueName
+ thisWeek
+
+
+ INEnumValueDisplayName
+ This Month
+ INEnumValueDisplayNameID
+ 1HCLUn
+ INEnumValueIndex
+ 3
+ INEnumValueName
+ thisMonth
+
+
+ INEnumValueDisplayName
+ This Year
+ INEnumValueDisplayNameID
+ rkviBd
+ INEnumValueIndex
+ 4
+ INEnumValueName
+ thisYear
+
+
+
+
INIntentDefinitionModelVersion
1.2
INIntentDefinitionNamespace
88xZPY
INIntentDefinitionSystemVersion
- 21G83
+ 21G115
INIntentDefinitionToolsBuildVersion
- 13F100
+ 14A309
INIntentDefinitionToolsVersion
- 13.4.1
+ 14.0
INIntents
-
+
+
+ INIntentCategory
+ information
+ INIntentDescriptionID
+ vsIoMZ
+ INIntentEligibleForWidgets
+
+ INIntentIneligibleForSuggestions
+
+ INIntentLastParameterTag
+ 2
+ INIntentName
+ StoreWidgetsConfig
+ INIntentParameters
+
+
+ INIntentParameterConfigurable
+
+ INIntentParameterDisplayName
+ Time Range
+ INIntentParameterDisplayNameID
+ Piwru2
+ INIntentParameterDisplayPriority
+ 1
+ INIntentParameterEnumType
+ IntentTimeRange
+ INIntentParameterEnumTypeNamespace
+ 88xZPY
+ INIntentParameterMetadata
+
+ INIntentParameterMetadataDefaultValue
+ today
+
+ INIntentParameterName
+ timeRange
+ INIntentParameterPromptDialogs
+
+
+ INIntentParameterPromptDialogCustom
+
+ INIntentParameterPromptDialogType
+ Configuration
+
+
+ INIntentParameterPromptDialogCustom
+
+ INIntentParameterPromptDialogType
+ Primary
+
+
+ INIntentParameterPromptDialogCustom
+
+ INIntentParameterPromptDialogFormatString
+ There are ${count} options matching ‘${timeRange}’.
+ INIntentParameterPromptDialogFormatStringID
+ 8m39ih
+ INIntentParameterPromptDialogType
+ DisambiguationIntroduction
+
+
+ INIntentParameterPromptDialogCustom
+
+ INIntentParameterPromptDialogFormatString
+ Just to confirm, you wanted ‘${timeRange}’?
+ INIntentParameterPromptDialogFormatStringID
+ SEcvUI
+ INIntentParameterPromptDialogType
+ Confirmation
+
+
+ INIntentParameterTag
+ 2
+ INIntentParameterType
+ Integer
+
+
+ INIntentResponse
+
+ INIntentResponseCodes
+
+
+ INIntentResponseCodeName
+ success
+ INIntentResponseCodeSuccess
+
+
+
+ INIntentResponseCodeName
+ failure
+
+
+
+ INIntentTitle
+ Store Widgets Config
+ INIntentTitleID
+ lYFT2L
+ INIntentType
+ Custom
+ INIntentVerb
+ View
+
+
INTypes
diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj
index 3275e84f5c8..ba97baed788 100644
--- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj
+++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj
@@ -1148,6 +1148,7 @@
AEA622B2274669D3002A9B57 /* AddOrderCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEA622B1274669D3002A9B57 /* AddOrderCoordinator.swift */; };
AEA622B427466B78002A9B57 /* BottomSheetOrderType.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEA622B327466B78002A9B57 /* BottomSheetOrderType.swift */; };
AEA622B727468790002A9B57 /* AddOrderCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEA622B627468790002A9B57 /* AddOrderCoordinatorTests.swift */; };
+ AEA7840428FEE82A000485FC /* StatsTimeRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEA7840328FEE82A000485FC /* StatsTimeRange.swift */; };
AEACCB6D2785FF4A000D01F0 /* NavigationRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEACCB6C2785FF4A000D01F0 /* NavigationRow.swift */; };
AEB73C0C25CD734200A8454A /* AttributePickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEB73C0B25CD734200A8454A /* AttributePickerViewModel.swift */; };
AEB73C1725CD8E5800A8454A /* AttributePickerViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEB73C1625CD8E5800A8454A /* AttributePickerViewModelTests.swift */; };
@@ -3046,6 +3047,7 @@
AEA622B1274669D3002A9B57 /* AddOrderCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddOrderCoordinator.swift; sourceTree = ""; };
AEA622B327466B78002A9B57 /* BottomSheetOrderType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomSheetOrderType.swift; sourceTree = ""; };
AEA622B627468790002A9B57 /* AddOrderCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddOrderCoordinatorTests.swift; sourceTree = ""; };
+ AEA7840328FEE82A000485FC /* StatsTimeRange.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatsTimeRange.swift; sourceTree = ""; };
AEACCB6C2785FF4A000D01F0 /* NavigationRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRow.swift; sourceTree = ""; };
AEB73C0B25CD734200A8454A /* AttributePickerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributePickerViewModel.swift; sourceTree = ""; };
AEB73C1625CD8E5800A8454A /* AttributePickerViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributePickerViewModelTests.swift; sourceTree = ""; };
@@ -5378,6 +5380,7 @@
265C99E028B9BA43005E6117 /* StoreInfoWidget.swift */,
265C99E328B9C834005E6117 /* StoreInfoProvider.swift */,
260DE20828CA7CE2009ECD7C /* StoreInfoDataService.swift */,
+ AEA7840328FEE82A000485FC /* StatsTimeRange.swift */,
265C99E528B9CB8E005E6117 /* StoreInfoViewModifiers.swift */,
3F1FA84728B60125009E246C /* StoreWidgets.intentdefinition */,
3F1FA84828B60126009E246C /* Assets.xcassets */,
@@ -9395,6 +9398,7 @@
AED9012D28E5F517002B4572 /* AppLinkWidget.swift in Sources */,
2608C50728C941D600C9DFC0 /* UserDefaults+Woo.swift in Sources */,
265C99E628B9CB8E005E6117 /* StoreInfoViewModifiers.swift in Sources */,
+ AEA7840428FEE82A000485FC /* StatsTimeRange.swift in Sources */,
2608C50628C93AB700C9DFC0 /* WooConstants.swift in Sources */,
260DE20A28CA7CFE009ECD7C /* StoreInfoDataService.swift in Sources */,
AE56E73428E76CDB00A1292B /* StoreInfoInlineWidget.swift in Sources */,