From f736175ed0849b3aeccea846ddb56b3525154345 Mon Sep 17 00:00:00 2001 From: Huong Do Date: Mon, 22 May 2023 10:55:21 +0700 Subject: [PATCH 01/15] Add a new view for celebrating first product --- .../Add Product/FirstProductCreatedView.swift | 15 +++++++++++++++ WooCommerce/WooCommerce.xcodeproj/project.pbxproj | 4 ++++ 2 files changed, 19 insertions(+) create mode 100644 WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift diff --git a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift new file mode 100644 index 00000000000..c344e7354b3 --- /dev/null +++ b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift @@ -0,0 +1,15 @@ +import SwiftUI + +/// Celebratory screen after creating the first product 🎉 +/// +struct FirstProductCreatedView: View { + var body: some View { + Text("Hello, World!") + } +} + +struct FirstProductCreatedView_Previews: PreviewProvider { + static var previews: some View { + FirstProductCreatedView() + } +} diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index c5045088841..2dd5580e13d 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -2056,6 +2056,7 @@ DE8C94662646990000C94823 /* PluginListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8C94652646990000C94823 /* PluginListViewController.swift */; }; DE8C946E264699B600C94823 /* PluginListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8C946D264699B600C94823 /* PluginListViewModel.swift */; }; DE971219290A9615000C0BD3 /* AddStoreFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE971218290A9615000C0BD3 /* AddStoreFooterView.swift */; }; + DE9F2D292A1B1AB2004E5957 /* FirstProductCreatedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9F2D282A1B1AB2004E5957 /* FirstProductCreatedView.swift */; }; DEA4269A2875440500265B0C /* PaymentMethodsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEA426992875440500265B0C /* PaymentMethodsScreen.swift */; }; DEC0293729C418FF00FD0E2F /* ApplicationPasswordAuthorizationWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC0293629C418FF00FD0E2F /* ApplicationPasswordAuthorizationWebViewController.swift */; }; DEC0293A29C41BC500FD0E2F /* ApplicationPasswordAuthorizationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC0293929C41BC500FD0E2F /* ApplicationPasswordAuthorizationViewModel.swift */; }; @@ -4362,6 +4363,7 @@ DE8C94652646990000C94823 /* PluginListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginListViewController.swift; sourceTree = ""; }; DE8C946D264699B600C94823 /* PluginListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginListViewModel.swift; sourceTree = ""; }; DE971218290A9615000C0BD3 /* AddStoreFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddStoreFooterView.swift; sourceTree = ""; }; + DE9F2D282A1B1AB2004E5957 /* FirstProductCreatedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstProductCreatedView.swift; sourceTree = ""; }; DEA426992875440500265B0C /* PaymentMethodsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentMethodsScreen.swift; sourceTree = ""; }; DEC0293629C418FF00FD0E2F /* ApplicationPasswordAuthorizationWebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationPasswordAuthorizationWebViewController.swift; sourceTree = ""; }; DEC0293929C41BC500FD0E2F /* ApplicationPasswordAuthorizationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationPasswordAuthorizationViewModel.swift; sourceTree = ""; }; @@ -5793,6 +5795,7 @@ isa = PBXGroup; children = ( 02ECD1E324FF5E0B00735BE5 /* AddProductCoordinator.swift */, + DE9F2D282A1B1AB2004E5957 /* FirstProductCreatedView.swift */, 02ECD1E524FFB4E900735BE5 /* ProductFactory.swift */, ); path = "Add Product"; @@ -12164,6 +12167,7 @@ 035DBA45292D0164003E5125 /* CollectOrderPaymentUseCase.swift in Sources */, 0282DD96233C960C006A5FDB /* SearchResultCell.swift in Sources */, 027F83ED29B046D2002688C6 /* DashboardTopPerformersViewModel.swift in Sources */, + DE9F2D292A1B1AB2004E5957 /* FirstProductCreatedView.swift in Sources */, 260C32BE2527A2DE00157BC2 /* IssueRefundViewModel.swift in Sources */, 2678897C270E6E8B00BD249E /* SimplePaymentsAmount.swift in Sources */, 02D29A9229F7C39200473D6D /* UIImage+Text.swift in Sources */, From 4bb3afa5126370ea7047e59645cebfb94923975e Mon Sep 17 00:00:00 2001 From: Huong Do Date: Mon, 22 May 2023 10:56:44 +0700 Subject: [PATCH 02/15] Add new dependency ConfettiSwiftUI --- .../xcshareddata/swiftpm/Package.resolved | 9 +++++++++ .../WooCommerce.xcodeproj/project.pbxproj | 17 +++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/WooCommerce.xcworkspace/xcshareddata/swiftpm/Package.resolved b/WooCommerce.xcworkspace/xcshareddata/swiftpm/Package.resolved index 549969dd8c9..de3a1157f17 100644 --- a/WooCommerce.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/WooCommerce.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -19,6 +19,15 @@ "version": "4.1.0" } }, + { + "package": "ConfettiSwiftUI", + "repositoryURL": "https://github.com/simibac/ConfettiSwiftUI.git", + "state": { + "branch": null, + "revision": "8d3a15d0aa2991e0761749b767ef8d89bca6275a", + "version": "1.0.1" + } + }, { "package": "Difference", "repositoryURL": "https://github.com/krzysztofzablocki/Difference.git", diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 2dd5580e13d..1a851bcc72a 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -2057,6 +2057,7 @@ DE8C946E264699B600C94823 /* PluginListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8C946D264699B600C94823 /* PluginListViewModel.swift */; }; DE971219290A9615000C0BD3 /* AddStoreFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE971218290A9615000C0BD3 /* AddStoreFooterView.swift */; }; DE9F2D292A1B1AB2004E5957 /* FirstProductCreatedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9F2D282A1B1AB2004E5957 /* FirstProductCreatedView.swift */; }; + DE9F2D2C2A1B1F5D004E5957 /* ConfettiSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = DE9F2D2B2A1B1F5D004E5957 /* ConfettiSwiftUI */; }; DEA4269A2875440500265B0C /* PaymentMethodsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEA426992875440500265B0C /* PaymentMethodsScreen.swift */; }; DEC0293729C418FF00FD0E2F /* ApplicationPasswordAuthorizationWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC0293629C418FF00FD0E2F /* ApplicationPasswordAuthorizationWebViewController.swift */; }; DEC0293A29C41BC500FD0E2F /* ApplicationPasswordAuthorizationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC0293929C41BC500FD0E2F /* ApplicationPasswordAuthorizationViewModel.swift */; }; @@ -4564,6 +4565,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + DE9F2D2C2A1B1F5D004E5957 /* ConfettiSwiftUI in Frameworks */, 3FFC5EAC2851942F00563C48 /* Charts in Frameworks */, D88FDB4525DD223B00CB0DBD /* Hardware.framework in Frameworks */, 263E37E12641AD8300260D3B /* Codegen in Frameworks */, @@ -10422,6 +10424,7 @@ 174CA86927D90A6200126524 /* AutomatticAbout */, 3FFC5EAB2851942F00563C48 /* Charts */, 4598298028574688003A9AFE /* Inject */, + DE9F2D2B2A1B1F5D004E5957 /* ConfettiSwiftUI */, ); productName = WooCommerce; productReference = B56DB3C62049BFAA00D4AA8E /* WooCommerce.app */; @@ -10592,6 +10595,7 @@ 3FFC5EAA2851942F00563C48 /* XCRemoteSwiftPackageReference "Charts" */, 4598297F28574688003A9AFE /* XCRemoteSwiftPackageReference "Inject" */, 3F2C8A17285B038800B1A5BB /* XCRemoteSwiftPackageReference "test-collector-swift" */, + DE9F2D2A2A1B1F5D004E5957 /* XCRemoteSwiftPackageReference "ConfettiSwiftUI" */, ); productRefGroup = B56DB3C72049BFAA00D4AA8E /* Products */; projectDirPath = ""; @@ -13941,6 +13945,14 @@ minimumVersion = 1.1.1; }; }; + DE9F2D2A2A1B1F5D004E5957 /* XCRemoteSwiftPackageReference "ConfettiSwiftUI" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/simibac/ConfettiSwiftUI.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -14026,6 +14038,11 @@ isa = XCSwiftPackageProductDependency; productName = TestKit; }; + DE9F2D2B2A1B1F5D004E5957 /* ConfettiSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + package = DE9F2D2A2A1B1F5D004E5957 /* XCRemoteSwiftPackageReference "ConfettiSwiftUI" */; + productName = ConfettiSwiftUI; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = B56DB3BE2049BFAA00D4AA8E /* Project object */; From 7512de018d88c6bcdd56df4f5350319b3b08df3e Mon Sep 17 00:00:00 2001 From: Huong Do Date: Mon, 22 May 2023 11:50:51 +0700 Subject: [PATCH 03/15] Add UI for the celebratory screen --- .../Add Product/FirstProductCreatedView.swift | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift index c344e7354b3..48089c2e4c0 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift @@ -4,12 +4,51 @@ import SwiftUI /// struct FirstProductCreatedView: View { var body: some View { - Text("Hello, World!") + VStack(spacing: Layout.verticalSpacing) { + Spacer() + VStack { + Text(Localization.title) + .titleStyle() + Text(Localization.message) + .secondaryBodyStyle() + } + Image(uiImage: .welcomeImage) + Button(Localization.shareAction) { + // TODO + } + .buttonStyle(PrimaryButtonStyle()) + Spacer() + } + .padding() + .background(Color(uiColor: .systemBackground)) + } +} + +private extension FirstProductCreatedView { + enum Layout { + static let verticalSpacing: CGFloat = 32 + } + enum Localization { + static let title = NSLocalizedString( + "Congratulations 🎉", + comment: "Title of the celebratory screen after creating the first product" + ) + static let message = NSLocalizedString( + "Great work on your first product!", + comment: "Message on the celebratory screen after creating first product" + ) + static let shareAction = NSLocalizedString( + "Spread the word", + comment: "Title of the action button to share the first created product" + ) } } struct FirstProductCreatedView_Previews: PreviewProvider { static var previews: some View { FirstProductCreatedView() + .environment(\.colorScheme, .light) + FirstProductCreatedView() + .environment(\.colorScheme, .dark) } } From a48d2410153f4c96ab50796f5a3c6596bed8c39e Mon Sep 17 00:00:00 2001 From: Huong Do Date: Mon, 22 May 2023 14:02:40 +0700 Subject: [PATCH 04/15] Add confetti for the celebratory view --- .../Add Product/FirstProductCreatedView.swift | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift index 48089c2e4c0..04e2b960033 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift @@ -1,40 +1,52 @@ +import ConfettiSwiftUI import SwiftUI /// Celebratory screen after creating the first product 🎉 /// struct FirstProductCreatedView: View { + @State private var confettiCounter: Int = 0 + var body: some View { - VStack(spacing: Layout.verticalSpacing) { - Spacer() - VStack { + GeometryReader { proxy in + VStack(spacing: Constants.verticalSpacing) { + Spacer() Text(Localization.title) .titleStyle() + Image(uiImage: .welcomeImage) Text(Localization.message) .secondaryBodyStyle() + Button(Localization.shareAction) { + // TODO + } + .buttonStyle(PrimaryButtonStyle()) + .padding(.horizontal) + Spacer() } - Image(uiImage: .welcomeImage) - Button(Localization.shareAction) { - // TODO - } - .buttonStyle(PrimaryButtonStyle()) - Spacer() + .padding() + .background(Color(uiColor: .systemBackground)) + .confettiCannon(counter: $confettiCounter, + num: Constants.confettiCount, + rainHeight: proxy.size.height, + radius: proxy.size.width) + } + .onAppear { + confettiCounter += 1 } - .padding() - .background(Color(uiColor: .systemBackground)) } } private extension FirstProductCreatedView { - enum Layout { - static let verticalSpacing: CGFloat = 32 + enum Constants { + static let verticalSpacing: CGFloat = 48 + static let confettiCount: Int = 100 } enum Localization { static let title = NSLocalizedString( - "Congratulations 🎉", + "First product created 🎉", comment: "Title of the celebratory screen after creating the first product" ) static let message = NSLocalizedString( - "Great work on your first product!", + "Congratulations! You're one step closer to get the new store ready.", comment: "Message on the celebratory screen after creating first product" ) static let shareAction = NSLocalizedString( From 488011e27e0aeb647b8dfd431cc3487d560877d0 Mon Sep 17 00:00:00 2001 From: Huong Do Date: Mon, 22 May 2023 14:13:14 +0700 Subject: [PATCH 05/15] Make view scrollable --- .../Products/Add Product/FirstProductCreatedView.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift index 04e2b960033..84d3a716c2c 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift @@ -15,6 +15,7 @@ struct FirstProductCreatedView: View { Image(uiImage: .welcomeImage) Text(Localization.message) .secondaryBodyStyle() + .multilineTextAlignment(.center) Button(Localization.shareAction) { // TODO } @@ -23,7 +24,7 @@ struct FirstProductCreatedView: View { Spacer() } .padding() - .background(Color(uiColor: .systemBackground)) + .scrollVerticallyIfNeeded() .confettiCannon(counter: $confettiCounter, num: Constants.confettiCount, rainHeight: proxy.size.height, @@ -32,12 +33,13 @@ struct FirstProductCreatedView: View { .onAppear { confettiCounter += 1 } + .background(Color(uiColor: .systemBackground)) } } private extension FirstProductCreatedView { enum Constants { - static let verticalSpacing: CGFloat = 48 + static let verticalSpacing: CGFloat = 40 static let confettiCount: Int = 100 } enum Localization { @@ -62,5 +64,6 @@ struct FirstProductCreatedView_Previews: PreviewProvider { .environment(\.colorScheme, .light) FirstProductCreatedView() .environment(\.colorScheme, .dark) + .previewInterfaceOrientation(.landscapeLeft) } } From 7373cc705f6e6c88e1963ce37e8a44c4997849ce Mon Sep 17 00:00:00 2001 From: Huong Do Date: Mon, 22 May 2023 14:18:40 +0700 Subject: [PATCH 06/15] Add hosting controller --- .../Add Product/FirstProductCreatedView.swift | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift index 84d3a716c2c..1b0849f1c9d 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift @@ -1,14 +1,43 @@ import ConfettiSwiftUI import SwiftUI +final class FirstProductCreatedHostingController: UIHostingController { + init(productURL: String) { + super.init(rootView: FirstProductCreatedView(productURL: productURL)) + } + + @available(*, unavailable) + required dynamic init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + configureTransparentNavigationBar() + navigationItem.leftBarButtonItem = UIBarButtonItem(title: Localization.cancel, style: .plain, target: self, action: #selector(dismissView)) + } + + @objc + private func dismissView() { + dismiss(animated: true) + } +} + +private extension FirstProductCreatedHostingController { + enum Localization { + static let cancel = NSLocalizedString("Cancel", comment: "Button to dismiss the site credential login screen") + } +} + /// Celebratory screen after creating the first product 🎉 /// struct FirstProductCreatedView: View { + let productURL: String @State private var confettiCounter: Int = 0 var body: some View { GeometryReader { proxy in - VStack(spacing: Constants.verticalSpacing) { + ScrollableVStack(spacing: Constants.verticalSpacing) { Spacer() Text(Localization.title) .titleStyle() @@ -24,7 +53,6 @@ struct FirstProductCreatedView: View { Spacer() } .padding() - .scrollVerticallyIfNeeded() .confettiCannon(counter: $confettiCounter, num: Constants.confettiCount, rainHeight: proxy.size.height, @@ -60,9 +88,9 @@ private extension FirstProductCreatedView { struct FirstProductCreatedView_Previews: PreviewProvider { static var previews: some View { - FirstProductCreatedView() + FirstProductCreatedView(productURL: "https://example.com") .environment(\.colorScheme, .light) - FirstProductCreatedView() + FirstProductCreatedView(productURL: "https://example.com") .environment(\.colorScheme, .dark) .previewInterfaceOrientation(.landscapeLeft) } From a1910b70b2b14fb97d122edc7a01d2bf7670f888 Mon Sep 17 00:00:00 2001 From: Huong Do Date: Mon, 22 May 2023 14:44:37 +0700 Subject: [PATCH 07/15] Handle sharing product --- .../Add Product/FirstProductCreatedView.swift | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift index 1b0849f1c9d..2f980c8b01e 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift @@ -2,8 +2,12 @@ import ConfettiSwiftUI import SwiftUI final class FirstProductCreatedHostingController: UIHostingController { - init(productURL: String) { - super.init(rootView: FirstProductCreatedView(productURL: productURL)) + init(productURL: URL) { + super.init(rootView: FirstProductCreatedView()) + rootView.onSharingProduct = { [weak self] in + guard let self else { return } + SharingHelper.shareURL(url: productURL, from: self.view, in: self) + } } @available(*, unavailable) @@ -25,14 +29,14 @@ final class FirstProductCreatedHostingController: UIHostingController Void = {} @State private var confettiCounter: Int = 0 var body: some View { @@ -45,11 +49,10 @@ struct FirstProductCreatedView: View { Text(Localization.message) .secondaryBodyStyle() .multilineTextAlignment(.center) - Button(Localization.shareAction) { - // TODO - } - .buttonStyle(PrimaryButtonStyle()) - .padding(.horizontal) + Button(Localization.shareAction, + action: onSharingProduct) + .buttonStyle(PrimaryButtonStyle()) + .padding(.horizontal) Spacer() } .padding() @@ -88,10 +91,11 @@ private extension FirstProductCreatedView { struct FirstProductCreatedView_Previews: PreviewProvider { static var previews: some View { - FirstProductCreatedView(productURL: "https://example.com") - .environment(\.colorScheme, .light) - FirstProductCreatedView(productURL: "https://example.com") - .environment(\.colorScheme, .dark) - .previewInterfaceOrientation(.landscapeLeft) + FirstProductCreatedView() + .environment(\.colorScheme, .light) + + FirstProductCreatedView() + .environment(\.colorScheme, .dark) + .previewInterfaceOrientation(.landscapeLeft) } } From e1e2c84aa99dab20a48892e8485bfe5db9e4ca27 Mon Sep 17 00:00:00 2001 From: Huong Do Date: Mon, 22 May 2023 15:28:28 +0700 Subject: [PATCH 08/15] Show celebratory view when the first product is created --- .../Dashboard/DashboardViewController.swift | 3 ++- .../StoreOnboardingCoordinator.swift | 5 ++-- .../Add Product/AddProductCoordinator.swift | 24 ++++++++++++++++--- .../Products/ProductsViewController.swift | 17 +++++++++---- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift index 1afcb5536ba..bb3f363e70e 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift @@ -471,7 +471,8 @@ private extension DashboardViewController { let coordinator = AddProductCoordinator(siteID: siteID, source: .productOnboarding, sourceView: announcementView, - sourceNavigationController: navigationController) + sourceNavigationController: navigationController, + isFirstProduct: true) coordinator.onProductCreated = { [weak self] _ in guard let self else { return } self.viewModel.announcementViewModel = nil // Remove the products onboarding banner diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Onboarding/StoreOnboardingCoordinator.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Onboarding/StoreOnboardingCoordinator.swift index 0acb219b255..da57084f914 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Onboarding/StoreOnboardingCoordinator.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Onboarding/StoreOnboardingCoordinator.swift @@ -86,9 +86,10 @@ private extension StoreOnboardingCoordinator { let coordinator = AddProductCoordinator(siteID: site.siteID, source: .storeOnboarding, sourceView: nil, - sourceNavigationController: navigationController) + sourceNavigationController: navigationController, + isFirstProduct: true) self.addProductCoordinator = coordinator - coordinator.onProductCreated = { [weak self] _ in + coordinator.onProductCreated = { [weak self] product in self?.onTaskCompleted(.addFirstProduct) } coordinator.start() diff --git a/WooCommerce/Classes/ViewRelated/Products/Add Product/AddProductCoordinator.swift b/WooCommerce/Classes/ViewRelated/Products/Add Product/AddProductCoordinator.swift index db9261955c8..326e16c0342 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Add Product/AddProductCoordinator.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Add Product/AddProductCoordinator.swift @@ -29,6 +29,7 @@ final class AddProductCoordinator: Coordinator { private let sourceView: UIView? private let productImageUploader: ProductImageUploaderProtocol private let storage: StorageManagerType + private let isFirstProduct: Bool /// ResultController to to track the current product count. /// @@ -48,7 +49,8 @@ final class AddProductCoordinator: Coordinator { sourceBarButtonItem: UIBarButtonItem, sourceNavigationController: UINavigationController, storage: StorageManagerType = ServiceLocator.storageManager, - productImageUploader: ProductImageUploaderProtocol = ServiceLocator.productImageUploader) { + productImageUploader: ProductImageUploaderProtocol = ServiceLocator.productImageUploader, + isFirstProduct: Bool) { self.siteID = siteID self.source = source self.sourceBarButtonItem = sourceBarButtonItem @@ -56,6 +58,7 @@ final class AddProductCoordinator: Coordinator { self.navigationController = sourceNavigationController self.productImageUploader = productImageUploader self.storage = storage + self.isFirstProduct = isFirstProduct } init(siteID: Int64, @@ -63,7 +66,8 @@ final class AddProductCoordinator: Coordinator { sourceView: UIView?, sourceNavigationController: UINavigationController, storage: StorageManagerType = ServiceLocator.storageManager, - productImageUploader: ProductImageUploaderProtocol = ServiceLocator.productImageUploader) { + productImageUploader: ProductImageUploaderProtocol = ServiceLocator.productImageUploader, + isFirstProduct: Bool) { self.siteID = siteID self.source = source self.sourceBarButtonItem = nil @@ -71,6 +75,7 @@ final class AddProductCoordinator: Coordinator { self.navigationController = sourceNavigationController self.productImageUploader = productImageUploader self.storage = storage + self.isFirstProduct = isFirstProduct } required init?(coder aDecoder: NSCoder) { @@ -235,7 +240,13 @@ private extension AddProductCoordinator { let viewModel = ProductFormViewModel(product: model, formType: .add, productImageActionHandler: productImageActionHandler) - viewModel.onProductCreated = onProductCreated + viewModel.onProductCreated = { [weak self] product in + guard let self else { return } + self.onProductCreated(product) + if self.isFirstProduct, let url = URL(string: product.permalink) { + self.showFirstProductCreatedView(productURL: url) + } + } let viewController = ProductFormViewController(viewModel: viewModel, eventLogger: ProductFormEventLogger(), productImageActionHandler: productImageActionHandler, @@ -289,4 +300,11 @@ private extension AddProductCoordinator { }() ServiceLocator.analytics.track(event: .ProductsOnboarding.productCreationTypeSelected(type: analyticsType)) } + + /// Presents the celebratory view for the first created product. + /// + func showFirstProductCreatedView(productURL: URL) { + let viewController = FirstProductCreatedHostingController(productURL: productURL) + navigationController.present(UINavigationController(rootViewController: viewController), animated: true) + } } diff --git a/WooCommerce/Classes/ViewRelated/Products/ProductsViewController.swift b/WooCommerce/Classes/ViewRelated/Products/ProductsViewController.swift index d24d02b1f7b..574251cee47 100644 --- a/WooCommerce/Classes/ViewRelated/Products/ProductsViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Products/ProductsViewController.swift @@ -179,6 +179,8 @@ final class ProductsViewController: UIViewController, GhostableViewController { private var subscriptions: Set = [] + private var addProductCoordinator: AddProductCoordinator? + deinit { NotificationCenter.default.removeObserver(self) } @@ -269,10 +271,12 @@ private extension ProductsViewController { } @objc func addProduct(_ sender: UIBarButtonItem) { - addProduct(sourceBarButtonItem: sender) + addProduct(sourceBarButtonItem: sender, isFirstProduct: false) } - func addProduct(sourceBarButtonItem: UIBarButtonItem? = nil, sourceView: UIView? = nil) { + func addProduct(sourceBarButtonItem: UIBarButtonItem? = nil, + sourceView: UIView? = nil, + isFirstProduct: Bool) { guard let navigationController = navigationController, sourceBarButtonItem != nil || sourceView != nil else { return } @@ -283,17 +287,20 @@ private extension ProductsViewController { coordinatingController = AddProductCoordinator(siteID: siteID, source: source, sourceBarButtonItem: sourceBarButtonItem, - sourceNavigationController: navigationController) + sourceNavigationController: navigationController, + isFirstProduct: isFirstProduct) } else if let sourceView = sourceView { coordinatingController = AddProductCoordinator(siteID: siteID, source: source, sourceView: sourceView, - sourceNavigationController: navigationController) + sourceNavigationController: navigationController, + isFirstProduct: isFirstProduct) } else { fatalError("No source view for adding a product") } coordinatingController.start() + self.addProductCoordinator = coordinatingController } } @@ -1022,7 +1029,7 @@ private extension ProductsViewController { details: details, buttonTitle: buttonTitle, onTap: { [weak self] button in - self?.addProduct(sourceView: button) + self?.addProduct(sourceView: button, isFirstProduct: true) }, onPullToRefresh: { [weak self] refreshControl in self?.pullToRefresh(sender: refreshControl) From d996f326ddc4047c1ce3c4fccf45d30ef8b3a56c Mon Sep 17 00:00:00 2001 From: Huong Do Date: Mon, 22 May 2023 15:29:34 +0700 Subject: [PATCH 09/15] Add missing param isFirstProduct --- .../Products/Add Product/AddProductCoordinatorTests.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/WooCommerce/WooCommerceTests/ViewRelated/Products/Add Product/AddProductCoordinatorTests.swift b/WooCommerce/WooCommerceTests/ViewRelated/Products/Add Product/AddProductCoordinatorTests.swift index 21e16695f3a..4bf2a163668 100644 --- a/WooCommerce/WooCommerceTests/ViewRelated/Products/Add Product/AddProductCoordinatorTests.swift +++ b/WooCommerce/WooCommerceTests/ViewRelated/Products/Add Product/AddProductCoordinatorTests.swift @@ -44,6 +44,7 @@ private extension AddProductCoordinatorTests { return AddProductCoordinator(siteID: 100, source: .productsTab, sourceView: view, - sourceNavigationController: navigationController) + sourceNavigationController: navigationController, + isFirstProduct: false) } } From 47aeb4eb150cdb14138afc51576dd506558efc9a Mon Sep 17 00:00:00 2001 From: Huong Do Date: Mon, 22 May 2023 16:44:38 +0700 Subject: [PATCH 10/15] Add tracks --- WooCommerce/Classes/Analytics/WooAnalyticsStat.swift | 4 ++++ .../Products/Add Product/FirstProductCreatedView.swift | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift index c04b6d700b4..865be3e445c 100644 --- a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift +++ b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift @@ -713,6 +713,10 @@ public enum WooAnalyticsStat: String { case productDescriptionAIGenerationSuccess = "product_description_ai_generation_success" case productDescriptionAIGenerationFailed = "product_description_ai_generation_failed" + // MARK: First created product events + case firstCreatedProductShown = "first_created_product_shown" + case firstCreatedProductShared = "first_created_product_shared" + // MARK: Jetpack Tunnel Events // case jetpackTunnelTimeout = "jetpack_tunnel_timeout" diff --git a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift index 2f980c8b01e..b8e9f66658f 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift @@ -7,6 +7,7 @@ final class FirstProductCreatedHostingController: UIHostingController Date: Mon, 22 May 2023 16:48:43 +0700 Subject: [PATCH 11/15] Update release notes --- RELEASE-NOTES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index bb5b5f87a90..f17a100ea43 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -2,6 +2,7 @@ 13.8 ----- +- [*] Add Products: A new view is display to celebrate when the first product is created in a store. [https://github.com/woocommerce/woocommerce-ios/pull/9790] 13.7 From c563c0b08f4a34c7956dbca4c5e226f00b77dad4 Mon Sep 17 00:00:00 2001 From: Huong Do Date: Mon, 22 May 2023 16:54:22 +0700 Subject: [PATCH 12/15] Update event name --- WooCommerce/Classes/Analytics/WooAnalyticsStat.swift | 2 +- .../Products/Add Product/FirstProductCreatedView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift index 865be3e445c..d08f6935cab 100644 --- a/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift +++ b/WooCommerce/Classes/Analytics/WooAnalyticsStat.swift @@ -715,7 +715,7 @@ public enum WooAnalyticsStat: String { // MARK: First created product events case firstCreatedProductShown = "first_created_product_shown" - case firstCreatedProductShared = "first_created_product_shared" + case firstCreatedProductShareTapped = "first_created_product_share_tapped" // MARK: Jetpack Tunnel Events // diff --git a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift index b8e9f66658f..42e74fa321a 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift @@ -7,7 +7,7 @@ final class FirstProductCreatedHostingController: UIHostingController Date: Mon, 22 May 2023 16:58:24 +0700 Subject: [PATCH 13/15] Remove redundant change --- .../Dashboard/Onboarding/StoreOnboardingCoordinator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Onboarding/StoreOnboardingCoordinator.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Onboarding/StoreOnboardingCoordinator.swift index da57084f914..358c5493cff 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Onboarding/StoreOnboardingCoordinator.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Onboarding/StoreOnboardingCoordinator.swift @@ -89,7 +89,7 @@ private extension StoreOnboardingCoordinator { sourceNavigationController: navigationController, isFirstProduct: true) self.addProductCoordinator = coordinator - coordinator.onProductCreated = { [weak self] product in + coordinator.onProductCreated = { [weak self] _ in self?.onTaskCompleted(.addFirstProduct) } coordinator.start() From 699773eb9d6b74e54d486e10565708e217ed2630 Mon Sep 17 00:00:00 2001 From: Huong Do Date: Tue, 23 May 2023 08:28:15 +0700 Subject: [PATCH 14/15] Update wording for the celebratory view message Co-authored-by: Sharma Elanthiraiyan --- .../Products/Add Product/FirstProductCreatedView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift index 42e74fa321a..1d62fb7f83d 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift @@ -81,7 +81,7 @@ private extension FirstProductCreatedView { comment: "Title of the celebratory screen after creating the first product" ) static let message = NSLocalizedString( - "Congratulations! You're one step closer to get the new store ready.", + "Congratulations! You're one step closer to getting the new store ready.", comment: "Message on the celebratory screen after creating first product" ) static let shareAction = NSLocalizedString( From 022a58fce196ca3b0372b48ede7c5cb4d9e38dd4 Mon Sep 17 00:00:00 2001 From: Huong Do Date: Tue, 23 May 2023 08:29:17 +0700 Subject: [PATCH 15/15] Update wording for the dismiss button on the first product created view --- .../Products/Add Product/FirstProductCreatedView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift index 42e74fa321a..c9d9a27f95f 100644 --- a/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift +++ b/WooCommerce/Classes/ViewRelated/Products/Add Product/FirstProductCreatedView.swift @@ -31,7 +31,7 @@ final class FirstProductCreatedHostingController: UIHostingController