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 */,