From b67ea2ac020835e5db446e4817bcf428376e0b18 Mon Sep 17 00:00:00 2001 From: Beemo Date: Wed, 15 Mar 2023 21:49:51 +0800 Subject: [PATCH 1/5] Extract statsUrl from private to extension for reusable with lock screen widget --- WordPress/WordPress.xcodeproj/project.pbxproj | 14 ++++++++++ .../Extensions/HomeWidgetData+StatsURL.swift | 27 +++++++++++++++++++ .../Views/StatsWidgetsView.swift | 27 ------------------- 3 files changed, 41 insertions(+), 27 deletions(-) create mode 100644 WordPress/WordPressStatsWidgets/Extensions/HomeWidgetData+StatsURL.swift diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 8db955a484eb..251d568fa6c6 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -2866,6 +2866,8 @@ C9F1D4B82706ED7C00BDF917 /* EditHomepageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F1D4B62706ED7C00BDF917 /* EditHomepageViewController.swift */; }; C9F1D4BA2706EEEB00BDF917 /* HomepageEditorNavigationBarManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F1D4B92706EEEB00BDF917 /* HomepageEditorNavigationBarManager.swift */; }; C9F1D4BB2706EEEB00BDF917 /* HomepageEditorNavigationBarManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F1D4B92706EEEB00BDF917 /* HomepageEditorNavigationBarManager.swift */; }; + C9FE382229C2040600D39841 /* HomeWidgetData+StatsURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE382129C2040600D39841 /* HomeWidgetData+StatsURL.swift */; }; + C9FE382329C2040D00D39841 /* HomeWidgetData+StatsURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE382129C2040600D39841 /* HomeWidgetData+StatsURL.swift */; }; CB1FD8D826E4BBAA00EDAF06 /* SharePostTypePickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB48172926E0D93D008C2D9B /* SharePostTypePickerViewController.swift */; }; CB48172A26E0D93D008C2D9B /* SharePostTypePickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB48172926E0D93D008C2D9B /* SharePostTypePickerViewController.swift */; }; CBF6201326E8FB520061A1F8 /* RemotePost+ShareData.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBF6201226E8FB520061A1F8 /* RemotePost+ShareData.swift */; }; @@ -8099,6 +8101,7 @@ C9D7DDBF2613B84500104E95 /* WordPress 119.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 119.xcdatamodel"; sourceTree = ""; }; C9F1D4B62706ED7C00BDF917 /* EditHomepageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditHomepageViewController.swift; sourceTree = ""; }; C9F1D4B92706EEEB00BDF917 /* HomepageEditorNavigationBarManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomepageEditorNavigationBarManager.swift; sourceTree = ""; }; + C9FE382129C2040600D39841 /* HomeWidgetData+StatsURL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HomeWidgetData+StatsURL.swift"; sourceTree = ""; }; CB1DAFB7DE085F2FF0314622 /* Pods-WordPressShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressShareExtension.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-WordPressShareExtension/Pods-WordPressShareExtension.debug.xcconfig"; sourceTree = ""; }; CB1FD8D926E605CF00EDAF06 /* Extensions 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Extensions 4.xcdatamodel"; sourceTree = ""; }; CB48172926E0D93D008C2D9B /* SharePostTypePickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharePostTypePickerViewController.swift; sourceTree = ""; }; @@ -11035,6 +11038,7 @@ 3F8EEC4D25B4817000EC9DAE /* StatsWidgets.swift */, 3F8EEC6F25B4849A00EC9DAE /* SiteListProvider.swift */, C9C21D7529BECFAE009F68E5 /* LockScreenWidgets */, + C9FE382029C203EE00D39841 /* Extensions */, 3FFDDCB925B8A65F008D5BDD /* Widgets */, 3FB34ABB25672A59001A74A6 /* Model */, 3FFDDC0325B89F0C008D5BDD /* Cache */, @@ -15654,6 +15658,14 @@ path = Views; sourceTree = ""; }; + C9FE382029C203EE00D39841 /* Extensions */ = { + isa = PBXGroup; + children = ( + C9FE382129C2040600D39841 /* HomeWidgetData+StatsURL.swift */, + ); + path = Extensions; + sourceTree = ""; + }; CC098B8116A9EB0400450976 /* HTML */ = { isa = PBXGroup; children = ( @@ -20453,6 +20465,7 @@ 0107E0D628F97D5000DE87DB /* AllTimeWidgetStats.swift in Sources */, 0107E0D728F97D5000DE87DB /* Sites.intentdefinition in Sources */, 0107E0D828F97D5000DE87DB /* LocalizableStrings.swift in Sources */, + C9FE382329C2040D00D39841 /* HomeWidgetData+StatsURL.swift in Sources */, 0107E0D928F97D5000DE87DB /* SFHFKeychainUtils.m in Sources */, 0107E0DA28F97D5000DE87DB /* ListViewData.swift in Sources */, 0107E0DB28F97D5000DE87DB /* Double+Stats.swift in Sources */, @@ -22310,6 +22323,7 @@ 3F5C863B25C9EA8200BABE64 /* AllTimeWidgetStats.swift in Sources */, 3FD675D925C87A15009AB3C1 /* Sites.intentdefinition in Sources */, 3FE77C8325B0CA89007DE9E5 /* LocalizableStrings.swift in Sources */, + C9FE382229C2040600D39841 /* HomeWidgetData+StatsURL.swift in Sources */, 3FCC8FD9256C911300810295 /* SFHFKeychainUtils.m in Sources */, 3FE20C3725CF211F00A15525 /* ListViewData.swift in Sources */, 3F71D5302548C2B200A4BA93 /* Double+Stats.swift in Sources */, diff --git a/WordPress/WordPressStatsWidgets/Extensions/HomeWidgetData+StatsURL.swift b/WordPress/WordPressStatsWidgets/Extensions/HomeWidgetData+StatsURL.swift new file mode 100644 index 000000000000..e08968880354 --- /dev/null +++ b/WordPress/WordPressStatsWidgets/Extensions/HomeWidgetData+StatsURL.swift @@ -0,0 +1,27 @@ +import Foundation + +extension HomeWidgetTodayData { + static let statsUrl = "https://wordpress.com/stats/day/" + + var statsURL: URL? { + URL(string: Self.statsUrl + "\(siteID)?source=widget") + } +} + + +extension HomeWidgetAllTimeData { + static let statsUrl = "https://wordpress.com/stats/insights/" + + var statsURL: URL? { + URL(string: Self.statsUrl + "\(siteID)?source=widget") + } +} + + +extension HomeWidgetThisWeekData { + static let statsUrl = "https://wordpress.com/stats/week/" + + var statsURL: URL? { + URL(string: Self.statsUrl + "\(siteID)?source=widget") + } +} diff --git a/WordPress/WordPressStatsWidgets/Views/StatsWidgetsView.swift b/WordPress/WordPressStatsWidgets/Views/StatsWidgetsView.swift index 7ff90e846eae..8a66d364f71b 100644 --- a/WordPress/WordPressStatsWidgets/Views/StatsWidgetsView.swift +++ b/WordPress/WordPressStatsWidgets/Views/StatsWidgetsView.swift @@ -96,33 +96,6 @@ private extension StatsWidgetsView { } } - -private extension HomeWidgetTodayData { - static let statsUrl = "https://wordpress.com/stats/day/" - - var statsURL: URL? { - URL(string: Self.statsUrl + "\(siteID)?source=widget") - } -} - - -private extension HomeWidgetAllTimeData { - static let statsUrl = "https://wordpress.com/stats/insights/" - - var statsURL: URL? { - URL(string: Self.statsUrl + "\(siteID)?source=widget") - } -} - - -private extension HomeWidgetThisWeekData { - static let statsUrl = "https://wordpress.com/stats/week/" - - var statsURL: URL? { - URL(string: Self.statsUrl + "\(siteID)?source=widget") - } -} - private extension StatsWidgetKind { var statsURL: URL? { switch self { From 5d4fd19bef83f14fec2cb97ddddfdae9e5c6be85 Mon Sep 17 00:00:00 2001 From: Beemo Date: Wed, 15 Mar 2023 21:55:19 +0800 Subject: [PATCH 2/5] Implement single stat view for today views widget Add view provider for handling building view for site selected status Add ViewModelMapper for converting viewModel from home widget data --- .../LockScreenSingleStatViewModel.swift | 9 +++ .../LockScreenWidgetViewModelMapper.swift | 45 +++++++++++++++ ...ckScreenSingleStatWidgetViewProvider.swift | 20 +++++++ .../Views/LockScreenSingleStatView.swift | 57 +++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 WordPress/WordPressStatsWidgets/LockScreenWidgets/Models/LockScreenSingleStatViewModel.swift create mode 100644 WordPress/WordPressStatsWidgets/LockScreenWidgets/Models/LockScreenWidgetViewModelMapper.swift create mode 100644 WordPress/WordPressStatsWidgets/LockScreenWidgets/ViewProvider/LockScreenSingleStatWidgetViewProvider.swift create mode 100644 WordPress/WordPressStatsWidgets/LockScreenWidgets/Views/LockScreenSingleStatView.swift diff --git a/WordPress/WordPressStatsWidgets/LockScreenWidgets/Models/LockScreenSingleStatViewModel.swift b/WordPress/WordPressStatsWidgets/LockScreenWidgets/Models/LockScreenSingleStatViewModel.swift new file mode 100644 index 000000000000..00c784bb60a9 --- /dev/null +++ b/WordPress/WordPressStatsWidgets/LockScreenWidgets/Models/LockScreenSingleStatViewModel.swift @@ -0,0 +1,9 @@ +import Foundation + +struct LockScreenSingleStatViewModel { + let siteName: String + let title: String + let value: String + let dateRange: String + let updatedTime: Date +} diff --git a/WordPress/WordPressStatsWidgets/LockScreenWidgets/Models/LockScreenWidgetViewModelMapper.swift b/WordPress/WordPressStatsWidgets/LockScreenWidgets/Models/LockScreenWidgetViewModelMapper.swift new file mode 100644 index 000000000000..729cbca50e65 --- /dev/null +++ b/WordPress/WordPressStatsWidgets/LockScreenWidgets/Models/LockScreenWidgetViewModelMapper.swift @@ -0,0 +1,45 @@ +import Foundation + +struct LockScreenWidgetViewModelMapper { + let data: HomeWidgetData + + func getLockScreenSingleStatViewModel(title: String, dateRange: String) -> LockScreenSingleStatViewModel { + LockScreenSingleStatViewModel( + siteName: getSiteName(), + title: title, + value: getViews(), + dateRange: dateRange, + updatedTime: data.date + ) + } + + // TODO: Add `LockScreenStatsWidgetData` in creating lock screen widget provider and entry PR + // define statsURL, views, date, siteName + // HomeWidgetTodayData, HomeWidgetAllTimeData, HomeWidgetThisWeekData conform to it + // to reduce the type converting + func getStateURL() -> URL? { + if let todayData = data as? HomeWidgetTodayData { + return todayData.statsURL + } else if let allTimeData = data as? HomeWidgetAllTimeData { + return allTimeData.statsURL + } else if let thisWeekData = data as? HomeWidgetThisWeekData { + return thisWeekData.statsURL + } else { + return nil + } + } + + func getSiteName() -> String { + data.siteName + } + + func getViews() -> String { + if let todayData = data as? HomeWidgetTodayData { + return todayData.stats.views.abbreviatedString() + } else if let allTimeData = data as? HomeWidgetAllTimeData { + return allTimeData.stats.views.abbreviatedString() + } else { + return "" + } + } +} diff --git a/WordPress/WordPressStatsWidgets/LockScreenWidgets/ViewProvider/LockScreenSingleStatWidgetViewProvider.swift b/WordPress/WordPressStatsWidgets/LockScreenWidgets/ViewProvider/LockScreenSingleStatWidgetViewProvider.swift new file mode 100644 index 000000000000..32d7a17c3183 --- /dev/null +++ b/WordPress/WordPressStatsWidgets/LockScreenWidgets/ViewProvider/LockScreenSingleStatWidgetViewProvider.swift @@ -0,0 +1,20 @@ +import SwiftUI + +@available(iOS 16.0, *) +struct LockScreenSingleStatWidgetViewProvider: LockScreenStatsWidgetsViewProvider { + typealias SiteSelectedView = LockScreenSingleStatView + + func buildSiteSelectedView(_ data: HomeWidgetData) -> LockScreenSingleStatView { + let mapper = LockScreenWidgetViewModelMapper(data: data) + let viewModel = mapper.getLockScreenSingleStatViewModel( + title: LocalizableStrings.viewsTitle, + dateRange: LocalizableStrings.todayWidgetTitle + ) + return LockScreenSingleStatView(viewModel: viewModel) + } + + func statsURL(_ data: HomeWidgetData) -> URL? { + let mapper = LockScreenWidgetViewModelMapper(data: data) + return mapper.getStateURL() + } +} diff --git a/WordPress/WordPressStatsWidgets/LockScreenWidgets/Views/LockScreenSingleStatView.swift b/WordPress/WordPressStatsWidgets/LockScreenWidgets/Views/LockScreenSingleStatView.swift new file mode 100644 index 000000000000..c2b5444858b0 --- /dev/null +++ b/WordPress/WordPressStatsWidgets/LockScreenWidgets/Views/LockScreenSingleStatView.swift @@ -0,0 +1,57 @@ +import SwiftUI +import WidgetKit + +@available(iOS 16.0, *) +struct LockScreenSingleStatView: View { + @Environment(\.widgetFamily) var family: WidgetFamily + let viewModel: LockScreenSingleStatViewModel + + var body: some View { + if family == .accessoryRectangular { + ZStack { + AccessoryWidgetBackground().cornerRadius(8) + VStack(alignment: .leading) { + Text(viewModel.siteName) + .frame(maxWidth: .infinity, alignment: .leading) + .font(.system(size: 11)) + .minimumScaleFactor(0.8) + .lineLimit(1) + Text(viewModel.value) + .frame(maxWidth: .infinity, alignment: .leading) + .font(.system(size: 20, weight: .bold)) + .minimumScaleFactor(0.5) + .foregroundColor(.white) + Text("\(viewModel.title) \(viewModel.dateRange)") + .frame(maxWidth: .infinity, alignment: .leading) + .font(.system(size: 11)) + .minimumScaleFactor(0.8) + } + .padding( + EdgeInsets(top: 4, leading: 8, bottom: 4, trailing: 8) + ) + } + } else { + Text("Not implemented for widget family \(family.debugDescription)") + } + } +} + +@available(iOS 16.0, *) +struct LockScreenSingleStatView_Previews: PreviewProvider { + static let viewModel = LockScreenSingleStatViewModel( + siteName: "My WordPress Site", + title: "Views", + value: "649", + dateRange: "Today", + updatedTime: Date() + ) + + static var previews: some View { + LockScreenSingleStatView( + viewModel: LockScreenSingleStatView_Previews.viewModel + ) + .previewContext( + WidgetPreviewContext(family: .accessoryRectangular) + ) + } +} From b1b8e495979288e33c60ea484bff55a7bbdfb1ad Mon Sep 17 00:00:00 2001 From: Beemo Date: Wed, 15 Mar 2023 21:56:31 +0800 Subject: [PATCH 3/5] Update LockScreenStatsWidgetsView body to get the view from view provider --- WordPress/WordPress.xcodeproj/project.pbxproj | 40 +++++++++++++++++++ .../LockScreenStatsWidget.swift | 5 ++- .../Views/LockScreenStatsWidgetsView.swift | 22 +++++++++- 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 251d568fa6c6..b9d5be289d14 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -2868,6 +2868,14 @@ C9F1D4BB2706EEEB00BDF917 /* HomepageEditorNavigationBarManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F1D4B92706EEEB00BDF917 /* HomepageEditorNavigationBarManager.swift */; }; C9FE382229C2040600D39841 /* HomeWidgetData+StatsURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE382129C2040600D39841 /* HomeWidgetData+StatsURL.swift */; }; C9FE382329C2040D00D39841 /* HomeWidgetData+StatsURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE382129C2040600D39841 /* HomeWidgetData+StatsURL.swift */; }; + C9FE382729C204C100D39841 /* LockScreenSingleStatWidgetViewProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE382629C204C100D39841 /* LockScreenSingleStatWidgetViewProvider.swift */; }; + C9FE382829C204C100D39841 /* LockScreenSingleStatWidgetViewProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE382629C204C100D39841 /* LockScreenSingleStatWidgetViewProvider.swift */; }; + C9FE382C29C204E700D39841 /* LockScreenWidgetViewModelMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE382A29C204E700D39841 /* LockScreenWidgetViewModelMapper.swift */; }; + C9FE382D29C204E700D39841 /* LockScreenWidgetViewModelMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE382A29C204E700D39841 /* LockScreenWidgetViewModelMapper.swift */; }; + C9FE382E29C204E700D39841 /* LockScreenSingleStatViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE382B29C204E700D39841 /* LockScreenSingleStatViewModel.swift */; }; + C9FE382F29C204E700D39841 /* LockScreenSingleStatViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE382B29C204E700D39841 /* LockScreenSingleStatViewModel.swift */; }; + C9FE383129C2053300D39841 /* LockScreenSingleStatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE383029C2053300D39841 /* LockScreenSingleStatView.swift */; }; + C9FE383229C2053300D39841 /* LockScreenSingleStatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE383029C2053300D39841 /* LockScreenSingleStatView.swift */; }; CB1FD8D826E4BBAA00EDAF06 /* SharePostTypePickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB48172926E0D93D008C2D9B /* SharePostTypePickerViewController.swift */; }; CB48172A26E0D93D008C2D9B /* SharePostTypePickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB48172926E0D93D008C2D9B /* SharePostTypePickerViewController.swift */; }; CBF6201326E8FB520061A1F8 /* RemotePost+ShareData.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBF6201226E8FB520061A1F8 /* RemotePost+ShareData.swift */; }; @@ -8102,6 +8110,10 @@ C9F1D4B62706ED7C00BDF917 /* EditHomepageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditHomepageViewController.swift; sourceTree = ""; }; C9F1D4B92706EEEB00BDF917 /* HomepageEditorNavigationBarManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomepageEditorNavigationBarManager.swift; sourceTree = ""; }; C9FE382129C2040600D39841 /* HomeWidgetData+StatsURL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HomeWidgetData+StatsURL.swift"; sourceTree = ""; }; + C9FE382629C204C100D39841 /* LockScreenSingleStatWidgetViewProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockScreenSingleStatWidgetViewProvider.swift; sourceTree = ""; }; + C9FE382A29C204E700D39841 /* LockScreenWidgetViewModelMapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockScreenWidgetViewModelMapper.swift; sourceTree = ""; }; + C9FE382B29C204E700D39841 /* LockScreenSingleStatViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockScreenSingleStatViewModel.swift; sourceTree = ""; }; + C9FE383029C2053300D39841 /* LockScreenSingleStatView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockScreenSingleStatView.swift; sourceTree = ""; }; CB1DAFB7DE085F2FF0314622 /* Pods-WordPressShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressShareExtension.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-WordPressShareExtension/Pods-WordPressShareExtension.debug.xcconfig"; sourceTree = ""; }; CB1FD8D926E605CF00EDAF06 /* Extensions 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Extensions 4.xcdatamodel"; sourceTree = ""; }; CB48172926E0D93D008C2D9B /* SharePostTypePickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharePostTypePickerViewController.swift; sourceTree = ""; }; @@ -15645,7 +15657,9 @@ isa = PBXGroup; children = ( C9C21D7629BECFC1009F68E5 /* LockScreenStatsWidget.swift */, + C9FE382929C204D700D39841 /* Models */, C9C21D8829BF4998009F68E5 /* Views */, + C9FE382529C204A500D39841 /* ViewProvider */, ); path = LockScreenWidgets; sourceTree = ""; @@ -15654,6 +15668,7 @@ isa = PBXGroup; children = ( C9C21D7A29BED18C009F68E5 /* LockScreenStatsWidgetsView.swift */, + C9FE383029C2053300D39841 /* LockScreenSingleStatView.swift */, ); path = Views; sourceTree = ""; @@ -15666,6 +15681,23 @@ path = Extensions; sourceTree = ""; }; + C9FE382529C204A500D39841 /* ViewProvider */ = { + isa = PBXGroup; + children = ( + C9FE382629C204C100D39841 /* LockScreenSingleStatWidgetViewProvider.swift */, + ); + path = ViewProvider; + sourceTree = ""; + }; + C9FE382929C204D700D39841 /* Models */ = { + isa = PBXGroup; + children = ( + C9FE382B29C204E700D39841 /* LockScreenSingleStatViewModel.swift */, + C9FE382A29C204E700D39841 /* LockScreenWidgetViewModelMapper.swift */, + ); + path = Models; + sourceTree = ""; + }; CC098B8116A9EB0400450976 /* HTML */ = { isa = PBXGroup; children = ( @@ -20423,6 +20455,7 @@ 0107E0B428F97D5000DE87DB /* Constants.m in Sources */, 01CE5012290A890B00A9C2E0 /* TracksConfiguration.swift in Sources */, C9C21D7C29BED18C009F68E5 /* LockScreenStatsWidgetsView.swift in Sources */, + C9FE382F29C204E700D39841 /* LockScreenSingleStatViewModel.swift in Sources */, 0107E0B528F97D5000DE87DB /* StatsWidgetEntry.swift in Sources */, 0107E0B628F97D5000DE87DB /* HomeWidgetCache.swift in Sources */, 0107E18C29000E2A00DE87DB /* AppStyleGuide.swift in Sources */, @@ -20452,10 +20485,12 @@ 0107E0CA28F97D5000DE87DB /* HomeWidgetThisWeekData.swift in Sources */, 0107E0CB28F97D5000DE87DB /* WordPressHomeWidgetAllTime.swift in Sources */, 0107E0CC28F97D5000DE87DB /* KeyValueDatabase.swift in Sources */, + C9FE382829C204C100D39841 /* LockScreenSingleStatWidgetViewProvider.swift in Sources */, 0107E0CD28F97D5000DE87DB /* CocoaLumberjack.swift in Sources */, 0107E0CE28F97D5000DE87DB /* ListRow.swift in Sources */, 0107E18A29000E1500DE87DB /* MurielColor.swift in Sources */, 0107E0D028F97D5000DE87DB /* WordPressHomeWidgetThisWeek.swift in Sources */, + C9FE382D29C204E700D39841 /* LockScreenWidgetViewModelMapper.swift in Sources */, 0107E0D128F97D5000DE87DB /* SingleStatView.swift in Sources */, 0107E0D228F97D5000DE87DB /* UnconfiguredView.swift in Sources */, 0107E1852900059300DE87DB /* LocalizationConfiguration.swift in Sources */, @@ -20465,6 +20500,7 @@ 0107E0D628F97D5000DE87DB /* AllTimeWidgetStats.swift in Sources */, 0107E0D728F97D5000DE87DB /* Sites.intentdefinition in Sources */, 0107E0D828F97D5000DE87DB /* LocalizableStrings.swift in Sources */, + C9FE383229C2053300D39841 /* LockScreenSingleStatView.swift in Sources */, C9FE382329C2040D00D39841 /* HomeWidgetData+StatsURL.swift in Sources */, 0107E0D928F97D5000DE87DB /* SFHFKeychainUtils.m in Sources */, 0107E0DA28F97D5000DE87DB /* ListViewData.swift in Sources */, @@ -22281,6 +22317,7 @@ 3F1FD30D2548B0A80060C53A /* Constants.m in Sources */, 01CE500C290A88BF00A9C2E0 /* TracksConfiguration.swift in Sources */, C9C21D7B29BED18C009F68E5 /* LockScreenStatsWidgetsView.swift in Sources */, + C9FE382E29C204E700D39841 /* LockScreenSingleStatViewModel.swift in Sources */, 3F63B93C258179D100F581BE /* StatsWidgetEntry.swift in Sources */, 3FA53E9D256571D800F4D9A2 /* HomeWidgetCache.swift in Sources */, 0107E18D29000E3300DE87DB /* AppStyleGuide.swift in Sources */, @@ -22310,10 +22347,12 @@ 3F5C86C025CA197500BABE64 /* WordPressHomeWidgetAllTime.swift in Sources */, 3F6BC07E25B247A4007369D3 /* KeyValueDatabase.swift in Sources */, 3F1FD27B2548AE900060C53A /* CocoaLumberjack.swift in Sources */, + C9FE382729C204C100D39841 /* LockScreenSingleStatWidgetViewProvider.swift in Sources */, 3FCF66FB25CAF8E00047F337 /* ListRow.swift in Sources */, 8323789828526E6D003F4443 /* AppConfiguration.swift in Sources */, 0107E18B29000E1700DE87DB /* MurielColor.swift in Sources */, 3F8B138F25D09AA5004FAC0A /* WordPressHomeWidgetThisWeek.swift in Sources */, + C9FE382C29C204E700D39841 /* LockScreenWidgetViewModelMapper.swift in Sources */, 3F5689F0254209790048A9E4 /* SingleStatView.swift in Sources */, 3FAA18CC25797B85002B1911 /* UnconfiguredView.swift in Sources */, 0107E1872900065500DE87DB /* LocalizationConfiguration.swift in Sources */, @@ -22323,6 +22362,7 @@ 3F5C863B25C9EA8200BABE64 /* AllTimeWidgetStats.swift in Sources */, 3FD675D925C87A15009AB3C1 /* Sites.intentdefinition in Sources */, 3FE77C8325B0CA89007DE9E5 /* LocalizableStrings.swift in Sources */, + C9FE383129C2053300D39841 /* LockScreenSingleStatView.swift in Sources */, C9FE382229C2040600D39841 /* HomeWidgetData+StatsURL.swift in Sources */, 3FCC8FD9256C911300810295 /* SFHFKeychainUtils.m in Sources */, 3FE20C3725CF211F00A15525 /* ListViewData.swift in Sources */, diff --git a/WordPress/WordPressStatsWidgets/LockScreenWidgets/LockScreenStatsWidget.swift b/WordPress/WordPressStatsWidgets/LockScreenWidgets/LockScreenStatsWidget.swift index dae513fe66cd..304c3055c9d0 100644 --- a/WordPress/WordPressStatsWidgets/LockScreenWidgets/LockScreenStatsWidget.swift +++ b/WordPress/WordPressStatsWidgets/LockScreenWidgets/LockScreenStatsWidget.swift @@ -24,7 +24,10 @@ struct LockScreenStatsWidget: Widget { widgetKind: .today ) ) { (entry: StatsWidgetEntry) -> LockScreenStatsWidgetsView in - return LockScreenStatsWidgetsView(timelineEntry: entry) + return LockScreenStatsWidgetsView( + timelineEntry: entry, + viewProvider: LockScreenSingleStatWidgetViewProvider() + ) } .configurationDisplayName(LocalizableStrings.todayWidgetTitle) .description(LocalizableStrings.todayPreviewDescription) diff --git a/WordPress/WordPressStatsWidgets/LockScreenWidgets/Views/LockScreenStatsWidgetsView.swift b/WordPress/WordPressStatsWidgets/LockScreenWidgets/Views/LockScreenStatsWidgetsView.swift index a6d960b76b03..12fdc0d8bb19 100644 --- a/WordPress/WordPressStatsWidgets/LockScreenWidgets/Views/LockScreenStatsWidgetsView.swift +++ b/WordPress/WordPressStatsWidgets/LockScreenWidgets/Views/LockScreenStatsWidgetsView.swift @@ -1,11 +1,29 @@ import SwiftUI import WidgetKit -struct LockScreenStatsWidgetsView: View { +protocol LockScreenStatsWidgetsViewProvider { + associatedtype SiteSelectedView: View + + @ViewBuilder + func buildSiteSelectedView(_ data: HomeWidgetData) -> SiteSelectedView + + func statsURL(_ data: HomeWidgetData) -> URL? +} + +struct LockScreenStatsWidgetsView: View { let timelineEntry: StatsWidgetEntry + let viewProvider: T @ViewBuilder var body: some View { - Text("Build Later") + switch timelineEntry { + case let .siteSelected(data, _): + viewProvider + .buildSiteSelectedView(data) + .widgetURL(viewProvider.statsURL(data)) + default: + // TODO: Build view for loggedOut, noSite, noData status + Text("Build Later") + } } } From 4b9566fd437033d0afdddb7f23cf7b81062e2db9 Mon Sep 17 00:00:00 2001 From: Beemo Date: Wed, 15 Mar 2023 22:06:27 +0800 Subject: [PATCH 4/5] Add mapper test to verify the viewModel converting from widget data correctly Add mapper and viewModel to WordPress target because app extension is not able to run unitTest --- WordPress/WordPress.xcodeproj/project.pbxproj | 18 ++++++ .../Widgets/WidgetsViewModelMapperTests.swift | 57 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 WordPress/WordPressTest/Widgets/WidgetsViewModelMapperTests.swift diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index b9d5be289d14..27e8ef43b1d9 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -2876,6 +2876,10 @@ C9FE382F29C204E700D39841 /* LockScreenSingleStatViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE382B29C204E700D39841 /* LockScreenSingleStatViewModel.swift */; }; C9FE383129C2053300D39841 /* LockScreenSingleStatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE383029C2053300D39841 /* LockScreenSingleStatView.swift */; }; C9FE383229C2053300D39841 /* LockScreenSingleStatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE383029C2053300D39841 /* LockScreenSingleStatView.swift */; }; + C9FE383729C2067E00D39841 /* WidgetsViewModelMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE383629C2067E00D39841 /* WidgetsViewModelMapperTests.swift */; }; + C9FE383829C206AE00D39841 /* LockScreenWidgetViewModelMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE382A29C204E700D39841 /* LockScreenWidgetViewModelMapper.swift */; }; + C9FE383929C2077700D39841 /* LockScreenSingleStatViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE382B29C204E700D39841 /* LockScreenSingleStatViewModel.swift */; }; + C9FE383A29C207F600D39841 /* HomeWidgetData+StatsURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9FE382129C2040600D39841 /* HomeWidgetData+StatsURL.swift */; }; CB1FD8D826E4BBAA00EDAF06 /* SharePostTypePickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB48172926E0D93D008C2D9B /* SharePostTypePickerViewController.swift */; }; CB48172A26E0D93D008C2D9B /* SharePostTypePickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB48172926E0D93D008C2D9B /* SharePostTypePickerViewController.swift */; }; CBF6201326E8FB520061A1F8 /* RemotePost+ShareData.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBF6201226E8FB520061A1F8 /* RemotePost+ShareData.swift */; }; @@ -8114,6 +8118,7 @@ C9FE382A29C204E700D39841 /* LockScreenWidgetViewModelMapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockScreenWidgetViewModelMapper.swift; sourceTree = ""; }; C9FE382B29C204E700D39841 /* LockScreenSingleStatViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockScreenSingleStatViewModel.swift; sourceTree = ""; }; C9FE383029C2053300D39841 /* LockScreenSingleStatView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockScreenSingleStatView.swift; sourceTree = ""; }; + C9FE383629C2067E00D39841 /* WidgetsViewModelMapperTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WidgetsViewModelMapperTests.swift; sourceTree = ""; }; CB1DAFB7DE085F2FF0314622 /* Pods-WordPressShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressShareExtension.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-WordPressShareExtension/Pods-WordPressShareExtension.debug.xcconfig"; sourceTree = ""; }; CB1FD8D926E605CF00EDAF06 /* Extensions 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Extensions 4.xcdatamodel"; sourceTree = ""; }; CB48172926E0D93D008C2D9B /* SharePostTypePickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharePostTypePickerViewController.swift; sourceTree = ""; }; @@ -15698,6 +15703,14 @@ path = Models; sourceTree = ""; }; + C9FE383329C2063900D39841 /* Widgets */ = { + isa = PBXGroup; + children = ( + C9FE383629C2067E00D39841 /* WidgetsViewModelMapperTests.swift */, + ); + path = Widgets; + sourceTree = ""; + }; CC098B8116A9EB0400450976 /* HTML */ = { isa = PBXGroup; children = ( @@ -16083,6 +16096,7 @@ 852416D01A12ED2D0030700C /* Utility */, BE20F5E11B2F738E0020694C /* ViewRelated */, 3F3D8548251E63DF001CA4D2 /* What's New */, + C9FE383329C2063900D39841 /* Widgets */, FF9839A71CD3960600E85258 /* WordPressAPI */, ); name = Tests; @@ -20867,6 +20881,7 @@ 4A072CD229093704006235BE /* AsyncBlockOperation.swift in Sources */, E185042F1EE6ABD9005C234C /* Restorer.swift in Sources */, 02761EC02270072F009BAF0F /* BlogDetailsViewController+SectionHelpers.swift in Sources */, + C9FE383829C206AE00D39841 /* LockScreenWidgetViewModelMapper.swift in Sources */, 984B138E21F65F870004B6A2 /* SiteStatsPeriodTableViewController.swift in Sources */, 433ADC1D223B2A7F00ED9DE1 /* TextBundleWrapper.m in Sources */, 2F605FAA25145F7200F99544 /* WPCategoryTree.swift in Sources */, @@ -21046,6 +21061,7 @@ 40232A9E230A6A740036B0B6 /* AbstractPost+HashHelpers.m in Sources */, FAE4201A1C5AEFE100C1D036 /* StartOverViewController.swift in Sources */, 0A3FCA1D28B71CBD00499A15 /* FullScreenCommentReplyViewModel.swift in Sources */, + C9FE383A29C207F600D39841 /* HomeWidgetData+StatsURL.swift in Sources */, 1790A4531E28F0ED00AE54C2 /* UINavigationController+Helpers.swift in Sources */, FEA088052696F7AA00193358 /* WPStyleGuide+List.swift in Sources */, 17C2FF0925D4852400CDB712 /* UnifiedProloguePages.swift in Sources */, @@ -22181,6 +22197,7 @@ B50EED791C0E5B2400D278CA /* SettingsPickerViewController.swift in Sources */, E1F5A1BC1771C90A00E0495F /* WPTableImageSource.m in Sources */, 17C1D6912670E4A2006C8970 /* UIFont+Fitting.swift in Sources */, + C9FE383929C2077700D39841 /* LockScreenSingleStatViewModel.swift in Sources */, 329F8E5824DDBD11002A5311 /* ReaderTopicCollectionViewCoordinator.swift in Sources */, 5948AD0E1AB734F2006E8882 /* WPAppAnalytics.m in Sources */, B53AD9BF1BE9584B009AB87E /* SettingsSelectionViewController.m in Sources */, @@ -23102,6 +23119,7 @@ 0879FC161E9301DD00E1EFC8 /* MediaTests.swift in Sources */, B556EFCB1DCA374200728F93 /* DictionaryHelpersTests.swift in Sources */, DC06DFF927BD52BE00969974 /* WeeklyRoundupBackgroundTaskTests.swift in Sources */, + C9FE383729C2067E00D39841 /* WidgetsViewModelMapperTests.swift in Sources */, 24C69A8B2612421900312D9A /* UserSettingsTests.swift in Sources */, 8B6BD55024293FBE00DB8F28 /* PrepublishingNudgesViewControllerTests.swift in Sources */, DC13DB7E293FD09F00E33561 /* StatsInsightsStoreTests.swift in Sources */, diff --git a/WordPress/WordPressTest/Widgets/WidgetsViewModelMapperTests.swift b/WordPress/WordPressTest/Widgets/WidgetsViewModelMapperTests.swift new file mode 100644 index 000000000000..ad717f94d7a3 --- /dev/null +++ b/WordPress/WordPressTest/Widgets/WidgetsViewModelMapperTests.swift @@ -0,0 +1,57 @@ +import XCTest +@testable import WordPress + +final class WidgetsViewModelMapperTests: XCTestCase { + func testSingleStatViewModel() { + let views = 649875 + let date = Date() + let title = "Views" + let dateRange = "Today" + let todayStats = makeTodayWidgetStats(views: views) + let data = makeTodayData(stats: todayStats, date: date) + + let sut = makeSUT(data) + let viewModel = sut.getLockScreenSingleStatViewModel( + title: title, + dateRange: dateRange + ) + + XCTAssertEqual(viewModel.siteName, data.siteName) + XCTAssertEqual(viewModel.title, title) + XCTAssertEqual(viewModel.value, views.abbreviatedString()) + XCTAssertEqual(viewModel.dateRange, dateRange) + XCTAssertEqual(viewModel.updatedTime, date) + } + + func testTodayViewsStatsURL() { + let todayStats = makeTodayWidgetStats(views: 649) + let data = makeTodayData(stats: todayStats, date: Date()) + + let sut = makeSUT(data) + let statsURL = sut.getStateURL() + + XCTAssertEqual(statsURL?.absoluteString, "https://wordpress.com/stats/day/0?source=widget") + } +} + +extension WidgetsViewModelMapperTests { + func makeSUT(_ data: HomeWidgetData) -> LockScreenWidgetViewModelMapper { + LockScreenWidgetViewModelMapper(data: data) + } + + func makeTodayData(stats: TodayWidgetStats, date: Date) -> HomeWidgetTodayData { + HomeWidgetTodayData(siteID: 0, + siteName: "My WordPress Site", + url: "", + timeZone: TimeZone.current, + date: date, + stats: stats) + } + + func makeTodayWidgetStats(views: Int) -> TodayWidgetStats { + TodayWidgetStats(views: views, + visitors: 572, + likes: 16, + comments: 8) + } +} From 831ac90e6fdb20102c7602cdd4941265b455bf60 Mon Sep 17 00:00:00 2001 From: Beemo Date: Thu, 16 Mar 2023 12:33:47 +0800 Subject: [PATCH 5/5] Fix getStatsURL typo --- .../Models/LockScreenWidgetViewModelMapper.swift | 2 +- .../ViewProvider/LockScreenSingleStatWidgetViewProvider.swift | 2 +- .../WordPressTest/Widgets/WidgetsViewModelMapperTests.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WordPress/WordPressStatsWidgets/LockScreenWidgets/Models/LockScreenWidgetViewModelMapper.swift b/WordPress/WordPressStatsWidgets/LockScreenWidgets/Models/LockScreenWidgetViewModelMapper.swift index 729cbca50e65..7e250fa60112 100644 --- a/WordPress/WordPressStatsWidgets/LockScreenWidgets/Models/LockScreenWidgetViewModelMapper.swift +++ b/WordPress/WordPressStatsWidgets/LockScreenWidgets/Models/LockScreenWidgetViewModelMapper.swift @@ -17,7 +17,7 @@ struct LockScreenWidgetViewModelMapper { // define statsURL, views, date, siteName // HomeWidgetTodayData, HomeWidgetAllTimeData, HomeWidgetThisWeekData conform to it // to reduce the type converting - func getStateURL() -> URL? { + func getStatsURL() -> URL? { if let todayData = data as? HomeWidgetTodayData { return todayData.statsURL } else if let allTimeData = data as? HomeWidgetAllTimeData { diff --git a/WordPress/WordPressStatsWidgets/LockScreenWidgets/ViewProvider/LockScreenSingleStatWidgetViewProvider.swift b/WordPress/WordPressStatsWidgets/LockScreenWidgets/ViewProvider/LockScreenSingleStatWidgetViewProvider.swift index 32d7a17c3183..49e6fe058a7f 100644 --- a/WordPress/WordPressStatsWidgets/LockScreenWidgets/ViewProvider/LockScreenSingleStatWidgetViewProvider.swift +++ b/WordPress/WordPressStatsWidgets/LockScreenWidgets/ViewProvider/LockScreenSingleStatWidgetViewProvider.swift @@ -15,6 +15,6 @@ struct LockScreenSingleStatWidgetViewProvider: LockScreenStatsWidgetsViewProvide func statsURL(_ data: HomeWidgetData) -> URL? { let mapper = LockScreenWidgetViewModelMapper(data: data) - return mapper.getStateURL() + return mapper.getStatsURL() } } diff --git a/WordPress/WordPressTest/Widgets/WidgetsViewModelMapperTests.swift b/WordPress/WordPressTest/Widgets/WidgetsViewModelMapperTests.swift index ad717f94d7a3..78ccef087670 100644 --- a/WordPress/WordPressTest/Widgets/WidgetsViewModelMapperTests.swift +++ b/WordPress/WordPressTest/Widgets/WidgetsViewModelMapperTests.swift @@ -28,7 +28,7 @@ final class WidgetsViewModelMapperTests: XCTestCase { let data = makeTodayData(stats: todayStats, date: Date()) let sut = makeSUT(data) - let statsURL = sut.getStateURL() + let statsURL = sut.getStatsURL() XCTAssertEqual(statsURL?.absoluteString, "https://wordpress.com/stats/day/0?source=widget") }