Skip to content

Commit

Permalink
Merge pull request #19304 from wordpress-mobile/Plans-in-site-creatio…
Browse files Browse the repository at this point in the history
…n-Plan-Selection-Screen

Plans in site creation: Plan selection screen
  • Loading branch information
irfano authored Oct 15, 2023
2 parents 4f0f2be + 05d9682 commit c54c4f7
Show file tree
Hide file tree
Showing 12 changed files with 537 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ object DomainRegistrationCheckoutWebViewNavigationDelegate {
UrlMatcher(
".*wordpress.com".toRegex(),
listOf(
"/jetpack-app".toRegex(),
"/plans.*?.*".toRegex(),
"/automattic-domain-name-registration-agreement.*".toRegex(),
"/checkout.*".toRegex(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import org.wordpress.android.ui.sitecreation.SiteCreationResult.Created
import org.wordpress.android.ui.sitecreation.SiteCreationResult.CreatedButNotFetched
import org.wordpress.android.ui.sitecreation.SiteCreationStep.DOMAINS
import org.wordpress.android.ui.sitecreation.SiteCreationStep.INTENTS
import org.wordpress.android.ui.sitecreation.SiteCreationStep.PLANS
import org.wordpress.android.ui.sitecreation.SiteCreationStep.PROGRESS
import org.wordpress.android.ui.sitecreation.SiteCreationStep.SITE_DESIGNS
import org.wordpress.android.ui.sitecreation.SiteCreationStep.SITE_NAME
Expand All @@ -43,6 +44,9 @@ import org.wordpress.android.ui.sitecreation.domains.DomainsScreenListener
import org.wordpress.android.ui.sitecreation.domains.SiteCreationDomainsFragment
import org.wordpress.android.ui.sitecreation.misc.OnHelpClickedListener
import org.wordpress.android.ui.sitecreation.misc.SiteCreationSource
import org.wordpress.android.ui.sitecreation.plans.PlanModel
import org.wordpress.android.ui.sitecreation.plans.PlansScreenListener
import org.wordpress.android.ui.sitecreation.plans.SiteCreationPlansFragment
import org.wordpress.android.ui.sitecreation.previews.SiteCreationPreviewFragment
import org.wordpress.android.ui.sitecreation.previews.SitePreviewViewModel
import org.wordpress.android.ui.sitecreation.progress.SiteCreationProgressFragment
Expand Down Expand Up @@ -70,6 +74,7 @@ class SiteCreationActivity : LocaleAwareActivity(),
IntentsScreenListener,
SiteNameScreenListener,
DomainsScreenListener,
PlansScreenListener,
OnHelpClickedListener,
BasicDialogPositiveClickInterface,
BasicDialogNegativeClickInterface {
Expand Down Expand Up @@ -220,6 +225,10 @@ class SiteCreationActivity : LocaleAwareActivity(),
mainViewModel.onDomainsScreenFinished(domain)
}

override fun onPlanSelected(plan: PlanModel) {
mainViewModel.onPlanSelection(plan)
}

override fun onHelpClicked(origin: Origin) {
ActivityLauncher.viewHelp(this, origin, null, null)
}
Expand All @@ -235,6 +244,7 @@ class SiteCreationActivity : LocaleAwareActivity(),
HomePagePickerFragment.newInstance(target.wizardState.siteIntent)
}
DOMAINS -> SiteCreationDomainsFragment.newInstance(screenTitle)
PLANS -> SiteCreationPlansFragment.newInstance(target.wizardState)
PROGRESS -> SiteCreationProgressFragment.newInstance(target.wizardState)
SITE_PREVIEW -> SiteCreationPreviewFragment.newInstance(screenTitle, target.wizardState)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import org.wordpress.android.ui.sitecreation.SiteCreationResult.NotCreated
import org.wordpress.android.ui.sitecreation.domains.DomainModel
import org.wordpress.android.ui.sitecreation.misc.SiteCreationSource
import org.wordpress.android.ui.sitecreation.misc.SiteCreationTracker
import org.wordpress.android.ui.sitecreation.plans.PlanModel
import org.wordpress.android.ui.sitecreation.usecases.FetchHomePageLayoutsUseCase
import org.wordpress.android.ui.utils.UiString.UiStringRes
import org.wordpress.android.util.AppLog
Expand Down Expand Up @@ -59,6 +60,7 @@ data class SiteCreationState(
val segmentId: Long? = null,
val siteDesign: String? = null,
val domain: DomainModel? = null,
val plan: PlanModel? = null,
val result: SiteCreationResult = NotCreated,
) : WizardState, Parcelable

Expand Down Expand Up @@ -260,13 +262,35 @@ class SiteCreationMainVM @Inject constructor(
siteCreationState = siteCreationState.copy(domain = null)
}
}
if (wizardStep == SiteCreationStep.PLANS) {
siteCreationState.plan?.let {
siteCreationState = siteCreationState.copy(plan = null)
}
}
}

fun onDomainsScreenFinished(domain: DomainModel) {
siteCreationState = siteCreationState.copy(domain = domain)
wizardManager.showNextStep()
}

fun onPlanSelection(plan: PlanModel) {
siteCreationState = siteCreationState.copy(plan = plan)
if (plan.productSlug == "free_plan") {
// if they select a paid domain, then choose a free plan, with free domain on plan selection screen
siteCreationState = siteCreationState.copy(
domain = DomainModel(
domainName = plan.productName.orEmpty(),
isFree = true,
cost = "",
productId = 0,
supportsPrivacy = false
)
)
}
wizardManager.showNextStep()
}

fun screenTitleForWizardStep(step: SiteCreationStep): SiteCreationScreenTitle {
val stepPosition = wizardManager.stepPosition(step)
val stepCount = wizardManager.stepsCount
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.wordpress.android.ui.sitecreation

import org.wordpress.android.ui.sitecreation.SiteCreationStep.DOMAINS
import org.wordpress.android.ui.sitecreation.SiteCreationStep.INTENTS
import org.wordpress.android.ui.sitecreation.SiteCreationStep.PLANS
import org.wordpress.android.ui.sitecreation.SiteCreationStep.PROGRESS
import org.wordpress.android.ui.sitecreation.SiteCreationStep.SITE_DESIGNS
import org.wordpress.android.ui.sitecreation.SiteCreationStep.SITE_NAME
Expand All @@ -13,7 +14,7 @@ import javax.inject.Inject
import javax.inject.Singleton

enum class SiteCreationStep : WizardStep {
SITE_DESIGNS, DOMAINS, PROGRESS, SITE_PREVIEW, INTENTS, SITE_NAME;
SITE_DESIGNS, DOMAINS, PLANS, PROGRESS, SITE_PREVIEW, INTENTS, SITE_NAME;
}

@Singleton
Expand All @@ -26,7 +27,7 @@ class SiteCreationStepsProvider @Inject constructor(

fun getSteps(): List<SiteCreationStep> = when {
isSiteNameEnabled -> listOf(INTENTS, SITE_NAME, SITE_DESIGNS, PROGRESS, SITE_PREVIEW)
isIntentsEnabled -> listOf(INTENTS, SITE_DESIGNS, DOMAINS, PROGRESS, SITE_PREVIEW)
else -> listOf(SITE_DESIGNS, DOMAINS, PROGRESS, SITE_PREVIEW)
isIntentsEnabled -> listOf(INTENTS, SITE_DESIGNS, DOMAINS, PLANS, PROGRESS, SITE_PREVIEW)
else -> listOf(SITE_DESIGNS, DOMAINS, PLANS, PROGRESS, SITE_PREVIEW)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.wordpress.android.ui.sitecreation.plans

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

interface PlansScreenListener {
fun onPlanSelected(plan: PlanModel)
}

@Parcelize
data class PlanModel(
val productId: Int?,
val productSlug: String?,
val productName: String?,
val isCurrentPlan: Boolean,
val hasDomainCredit: Boolean
) : Parcelable
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
package org.wordpress.android.ui.sitecreation.plans

import android.annotation.SuppressLint
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.WebView
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.viewmodel.compose.viewModel
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.wordpress.android.R
import org.wordpress.android.ui.WPWebViewActivity
import org.wordpress.android.ui.compose.components.MainTopAppBar
import org.wordpress.android.ui.compose.components.NavigationIcons
import org.wordpress.android.ui.compose.theme.AppTheme
import org.wordpress.android.ui.compose.utils.uiStringText
import org.wordpress.android.ui.main.jetpack.migration.compose.state.LoadingState
import org.wordpress.android.ui.sitecreation.SiteCreationActivity.Companion.ARG_STATE
import org.wordpress.android.ui.sitecreation.SiteCreationState
import org.wordpress.android.ui.sitecreation.plans.SiteCreationPlansWebViewClient.SiteCreationPlansWebViewClientListener
import org.wordpress.android.util.extensions.getParcelableCompat

@AndroidEntryPoint
class SiteCreationPlansFragment : Fragment(), SiteCreationPlansWebViewClientListener {
private val viewModel: SiteCreationPlansViewModel by viewModels()

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View = ComposeView(requireContext()).apply {
setContent {
AppTheme {
SiteCreationPlansPage(
navigationUp = requireActivity().onBackPressedDispatcher::onBackPressed
)
}
}
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

viewModel.start(requireNotNull(requireArguments().getParcelableCompat(ARG_STATE)))
viewModel.actionEvents.onEach(this::handleActionEvents).launchIn(viewLifecycleOwner.lifecycleScope)
}

private fun handleActionEvents(actionEvent: SiteCreationPlansActionEvent) {
when (actionEvent) {
is SiteCreationPlansActionEvent.CreateSite -> {
(requireActivity() as PlansScreenListener).onPlanSelected(actionEvent.planModel)
}
}
}

// SiteCreationWebViewClient
override fun onPlanSelected(uri: Uri) {
viewModel.onPlanSelected(uri)
}

override fun onWebViewPageLoaded() {
viewModel.onUrlLoaded()
}

override fun onWebViewReceivedError() {
viewModel.onWebViewError()
}

@Composable
@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
fun SiteCreationPlansPage(
navigationUp: () -> Unit = { },
viewModel: SiteCreationPlansViewModel = viewModel(),
) {
val uiState by viewModel.uiState.collectAsState()
Scaffold(
topBar = {
MainTopAppBar(
title = stringResource(R.string.site_creation_plans_selection_title),
navigationIcon = NavigationIcons.BackIcon,
onNavigationIconClick = navigationUp
)
},
content = { SiteCreationPlansContent(uiState) }
)
}

@SuppressLint("SetJavaScriptEnabled")
@Composable
private fun SiteCreationPlansContent(uiState: SiteCreationPlansUiState) {
when (uiState) {
is SiteCreationPlansUiState.Preparing -> LoadingState()
is SiteCreationPlansUiState.Prepared,
is SiteCreationPlansUiState.Loaded -> SiteCreationPlansWebView(uiState)
is SiteCreationPlansUiState.Error -> SiteCreationPlansError(uiState)
}
}

@Composable
fun SiteCreationPlansError(error: SiteCreationPlansUiState.Error) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.padding(20.dp)
.fillMaxWidth()
.fillMaxHeight(),
) {
Text(
text = uiStringText(uiString = error.title),
style = MaterialTheme.typography.h5,
textAlign = TextAlign.Center
)
Text(
text = uiStringText(uiString = error.description),
style = MaterialTheme.typography.body1,
textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 8.dp)
)
if (error.button != null) {
Button(
modifier = Modifier.padding(top = 8.dp),
onClick = error.button.click
) {
Text(text = uiStringText(uiString = error.button.text))
}
}
}
}

@SuppressLint("SetJavaScriptEnabled")
@Composable
private fun SiteCreationPlansWebView(uiState: SiteCreationPlansUiState) {
var webView: WebView? by remember { mutableStateOf(null) }

if (uiState is SiteCreationPlansUiState.Prepared) {
val model = uiState.model
LaunchedEffect(true) {
webView = WebView(requireContext()).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
scrollBarStyle = View.SCROLLBARS_INSIDE_OVERLAY
settings.userAgentString = model.userAgent
settings.javaScriptEnabled = model.enableJavascript
settings.domStorageEnabled = model.enableDomStorage
webViewClient = SiteCreationPlansWebViewClient(this@SiteCreationPlansFragment)
postUrl(WPWebViewActivity.WPCOM_LOGIN_URL, model.addressToLoad.toByteArray())
}
}
}

Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
if (uiState is SiteCreationPlansUiState.Prepared) {
LoadingState()
} else {
webView?.let { theWebView ->
AndroidView(
factory = { theWebView },
modifier = Modifier.fillMaxSize()
)
}
}
}
}

companion object {
const val TAG = "site_creation_plans_fragment_tag"

fun newInstance(siteCreationState: SiteCreationState) = SiteCreationPlansFragment().apply {
arguments = Bundle().apply {
putParcelable(ARG_STATE, siteCreationState)
}
}
}
}
Loading

0 comments on commit c54c4f7

Please sign in to comment.