Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Domain Management: Implement add new freestanding domain flow #21979

Merged
merged 28 commits into from
Nov 8, 2023

Conversation

hassaanelgarem
Copy link
Contributor

Closes #21778
Closes #21780
WPKit PR: wordpress-mobile/WordPressKit-iOS#644

Description

This PR adds the first steps for adding a domain which is searching for a new domain. It also integrates with the already implemented "Choice" screen and hooks up the freestanding domain purchase flow.

Recording

Simulator.Screen.Recording.-.iPhone.14.Pro.-.2023-11-06.at.08.30.18.mp4

Testing Instructions

  1. Setup the store sandbox
  2. Navigate to the "Me" tab
  3. Tap on "My Domains"
  4. Tap on the + button
  5. ✅ Make sure that the search domains screen is displayed as a modal
  6. Search for a domain
  7. ✅ Make sure that the search works fine
  8. Select a .live domain
  9. Tap on "Select Domain"
  10. ✅ Make sure the purchase choice screen is displayed
  11. Tap on "Get Domain"
  12. ✅ Make sure the checkout page is displayed (P.S: There is a delay here, and ideally we should display an activity indicator. Check Domain choice screen enhancements #21978 )
  13. Go through the checkout flow and purchase the domain
  14. ✅ Make sure the modal is dismissed after purchasing is successful and the domains list is updated

Other than the above flow, please testing the existing domain purchasing flows for regressions:

  • My Site -> Domains
  • Dashboard -> Free domain with plan card
  • Site creation

Regression Notes

  1. Potential unintended areas of impact
    Other places where a domain can be purchased:
  2. What I did to test those areas of impact (or what existing automated tests I relied on)
    Testing manually
  3. What automated tests I added (or what prevented me from doing so)
    N/A

PR submission checklist:

  • I have completed the Regression Notes.
  • I have considered adding unit tests for my changes.
  • I have considered adding accessibility improvements for my changes.
  • I have considered if this change warrants user-facing release notes and have added them to RELEASE-NOTES.txt if necessary.

UI Changes testing checklist:

  • Portrait and landscape orientations.
  • Light and dark modes.
  • Fonts: Larger, smaller and bold text.
  • High contrast.
  • VoiceOver.
  • Languages with large words or with letters/accents not frequently used in English.
  • Right-to-left languages. (Even if translation isn’t complete, formatting should still respect the right-to-left layout)
  • iPhone and iPad.
  • Multi-tasking: Split view and Slide over. (iPad)

@peril-wordpress-mobile
Copy link

peril-wordpress-mobile bot commented Nov 6, 2023

Warnings
⚠️ PR has more than 500 lines of code changing. Consider splitting into smaller PRs if possible.

Generated by 🚫 dangerJS

@wpmobilebot
Copy link
Contributor

wpmobilebot commented Nov 6, 2023

Jetpack Alpha📲 You can test the changes from this Pull Request in Jetpack Alpha by scanning the QR code below to install the corresponding build.
App NameJetpack Alpha Jetpack Alpha
ConfigurationRelease-Alpha
Build Numberpr21979-ac5cbec
Version23.6
Bundle IDcom.jetpack.alpha
Commitac5cbec
App Center Buildjetpack-installable-builds #6772
Automatticians: You can use our internal self-serve MC tool to give yourself access to App Center if needed.

@wpmobilebot
Copy link
Contributor

wpmobilebot commented Nov 6, 2023

WordPress Alpha📲 You can test the changes from this Pull Request in WordPress Alpha by scanning the QR code below to install the corresponding build.
App NameWordPress Alpha WordPress Alpha
ConfigurationRelease-Alpha
Build Numberpr21979-ac5cbec
Version23.6
Bundle IDorg.wordpress.alpha
Commitac5cbec
App Center BuildWPiOS - One-Offs #7755
Automatticians: You can use our internal self-serve MC tool to give yourself access to App Center if needed.


// MARK: Public Functions

func createCart(_ domain: FullyQuotedDomainSuggestion,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ What's the difference between the passed-in domain param and the property self.domain?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question!
No difference, really. It makes sense to remove this param and depend on the property for consistency.
Resolved in 42c5b70

let canOpenNewURL = newURL.absoluteString.starts(with: Constants.checkoutWebAddress)

guard canOpenNewURL else {
onCancel()
Copy link
Contributor

@salimbraksa salimbraksa Nov 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] There is a critical bug in this implementation. Many times when I was testing, the default account authentication would fail and the Log In page shows up instead of the Checkout page. And since the Log In url doesn't start with Constants.checkoutWebAddress, the web view is dismissed immediately.

I'm still not sure exactly why the webview authentication fails, but sometimes I could reproduce it when using the proxy ( and other times happens without proxy ). Here is the Sentry issue associated with this error.

I'm not sure exactly how to handle this edge case. In SiteCreationPurchasingWebFlowController, I haven't thought too much about this issue because the Domain Purchasing during Site Creation project was just an experiment.

Let me know what you think! 🙇

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting! Any idea how I can reproduce this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hassaanelgarem I couldn't figure out a way to consistently reproduce that issue, but I remember it happened to many times during development ( using proxy ) and a fewer times in production ( without proxy ).

What do you say we start by logging this event on Sentry or Tracks instead of silently dismissing the Checkout screen?

We can iterate on this later if we see that this happens very frequently.

Also, when logging this event, we should also include the URL that caused the web view to be dismissed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed on Slack, we suspect this to be a proxy issue. I am hesitant to add error tracking for this, as this is not technically an error. It's only an error if the user is redirected to log in, but in other cases, it is an expected behavior. What do you think?


let webViewController = WebViewControllerFactory.controllerWithDefaultAccountAndSecureInteraction(
url: url,
source: "domains_register", // TODO: Update source
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget to follow up on this @hassaanelgarem 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created this issue to track this: #21996 👍


let domainPurchasedCallback = { (domainViewController: UIViewController, domainName: String) in
allDomainsViewController.reloadDomains()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] @hassaanelgarem This closure is capturing allDomainsViewController. I'm not sure if that's going to cause a memory leak or not, what do you think of adding [weak allDomainsViewController] to be safe?

Feel free to ignore this suggestion if you think this is not going to a cause a memory leak.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for raising this! I don't think it's needed because allDomainsViewController doesn't have a strong reference to this closure. I've confirmed this using the Memory graph 👍

shouldPush: true)
}

func presentWebViewForCurrentSite(on viewController: UIViewController) {
Copy link
Contributor

@salimbraksa salimbraksa Nov 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ I'm curious if we should have a single presentCheckoutWebView method that handles site vs no-site logic based on whether self.site is nil or not. Because right now, it is programmatically possible to:

  • Call presentWebViewForCurrentSite even though self.site is nil. Which would result in an error.
  • Call presentWebViewForNoSite even though self.site is not nil. This wouldn't result in an error, but it might be confusing. We are presenting the Checkout web view for no-site even though the coordinator has a site?

I'm wondering whether it's possible to have an API design where it's programmatically impossible to misuse this coordinator.

Update
Please reply to this comment instead.


func handleExistingSiteChoice(on viewController: UIViewController) {
print("handleExistingSiteChoice")
}
Copy link
Contributor

@salimbraksa salimbraksa Nov 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding my earlier comment, I just saw these handleNoSiteChoice and handleExistingSiteChoice. So basically the coordinator has 5 public methods:

  • createCart
  • presentWebViewForNoSite
  • presentWebViewForCurrentSite
  • handleNoSiteChoice
  • handleExistingSiteChoice

In my opinion, it is not clear when to use which, and I think there is room for improvements here, for example:

  • Cart creation is an implementation detail and should be called internally. Similar to what you did in handleNoSiteChoice. So I think createCart method should be private.
  • presentWebViewForNoSite and presentWebViewForCurrentSite can also be private and called within handleNoSiteChoice and handleExistingSiteChoice.
  • Maybe handleNoSiteChoice and handleExistingSiteChoice can be merged into a single public method and handle site vs no-site internally depending on nullability of self.site.

I'm not suggesting to make any changes right now, because that would break the existing "My Site > Domains" implementation. But I just wanted to hear you thoughts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking the time to make these thoughtful suggestions!

I do agree with most of this. And I have tried to address these suggestions in #21998, which builds on this PR, specifically in af5cf72. I did this to avoid causing conflicts.

I didn't merge handleNoSiteChoice and handleExistingSiteChoice because I think it would complicate things. But I applied your other suggestions.

Let me know what you think! Either here or in #21998 🙇

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't merge handleNoSiteChoice and handleExistingSiteChoice because I think it would complicate things.

To clarify, there is what I meant by merging those 2 methods:

  1. Mark handleNoSiteChoice and handleExistingSiteChoice as private.
  2. Add a new public method that decides whether to call handleExistingSiteChoice or handleNoSiteChoice based on self.site
func handleDomainPurchasing() {
      if let site {
          handleExistingSiteChoice(site)
      } else {
           handleNoSiteChoice()
      }
}

private func handleNoSiteChoice() {
   ...
}

private func handleExistingSiteChoice(_ site: Blog) {
   ...
}

But I don't have a strong opinion on this, so feel free to decide whether you want make the change.

@salimbraksa
Copy link
Contributor

@hassaanelgarem I've dropped a few more comments but the feature works as described in the test instructions. 👍

@wpmobilebot
Copy link
Contributor

1 Warning
⚠️ This PR is larger than 500 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.

Generated by 🚫 Danger

Copy link
Contributor

@salimbraksa salimbraksa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@hassaanelgarem hassaanelgarem merged commit 0c8b031 into trunk Nov 8, 2023
23 checks passed
@hassaanelgarem hassaanelgarem deleted the task/21778-search-for-new-domain branch November 8, 2023 16:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

🍎 Purchase a freestanding domain flow 🍎 Search for a new domain screen
3 participants