Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import androidx.navigation.NavDestination
import androidx.navigation.NavOptions
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.navOptions
import com.automattic.android.tracks.crashlogging.CrashLogging
import com.google.android.material.appbar.AppBarLayout
import com.woocommerce.android.AppPrefs
Expand Down Expand Up @@ -183,13 +184,17 @@ class MainActivity :
@Inject
lateinit var trialStatusBarFormatterFactory: TrialStatusBarFormatterFactory

@Inject lateinit var animatorHelper: MainAnimatorHelper
@Inject
lateinit var animatorHelper: MainAnimatorHelper

@Inject lateinit var edgeToEdgeHelper: MainActivityEdgeToEdgeHelper
@Inject
lateinit var edgeToEdgeHelper: MainActivityEdgeToEdgeHelper

@Inject lateinit var posTabController: WooPosTabController
@Inject
lateinit var posTabController: WooPosTabController

@Inject lateinit var bookingsTabController: BookingsTabController
@Inject
lateinit var bookingsTabController: BookingsTabController

private val viewModel: MainActivityViewModel by viewModels()

Expand Down Expand Up @@ -1023,26 +1028,25 @@ class MainActivity :
restart()
}

override fun showProductDetail(remoteProductId: Long, popUpToProductList: Boolean) {
val action = when (popUpToProductList) {
true -> NavGraphMainDirections.actionGlobalProductDetailFragmentPopUpToProductList(
mode = ProductDetailFragment.Mode.ShowProduct(remoteProductId),
)
else -> NavGraphMainDirections.actionGlobalProductDetailFragment(
mode = ProductDetailFragment.Mode.ShowProduct(remoteProductId),
)
}
navController.navigateSafely(action)
}

override fun showProductDetailWithSharedTransition(remoteProductId: Long, sharedView: View) {
val productCardDetailTransitionName = getString(R.string.product_card_detail_transition_name)
val extras = FragmentNavigatorExtras(sharedView to productCardDetailTransitionName)

override fun showProductDetail(remoteProductId: Long, popUpToProductList: Boolean, sharedView: View?) {
val action = NavGraphMainDirections.actionGlobalProductDetailFragment(
mode = ProductDetailFragment.Mode.ShowProduct(remoteProductId),
)
navController.navigateSafely(directions = action, extras = extras)
val extras = if (sharedView != null) {
val productCardDetailTransitionName = getString(R.string.product_card_detail_transition_name)
FragmentNavigatorExtras(sharedView to productCardDetailTransitionName)
} else {
null
}
navController.navigateSafely(
directions = action,
extras = extras,
navOptions = navOptions {
if (popUpToProductList) {
popUpTo(R.id.products)
}
}
)
}

override fun showProductVariationDetail(remoteProductId: Long, remoteVariationId: Long) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ interface MainNavigationRouter {
fun isAtNavigationRoot(): Boolean
fun isChildFragmentShowing(): Boolean

fun showProductDetail(remoteProductId: Long, popUpToProductList: Boolean = false)
fun showProductDetailWithSharedTransition(
fun showProductDetail(
remoteProductId: Long,
sharedView: View,
popUpToProductList: Boolean = false,
sharedView: View? = null
)

fun showProductVariationDetail(remoteProductId: Long, remoteVariationId: Long)

fun showOrderDetail(
Expand All @@ -36,6 +37,7 @@ interface MainNavigationRouter {
launchedFromNotification: Boolean,
tempStatus: String? = null
)

fun showReviewDetailWithSharedTransition(
remoteReviewId: Long,
launchedFromNotification: Boolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ enum class ProductType(@StringRes val stringResource: Int = 0, val value: String
BUNDLE(R.string.product_type_bundle, CoreProductType.BUNDLE.value),
COMPOSITE(R.string.product_type_composite, "composite"),
VARIATION(R.string.product_type_variation, "variation"),
BOOKING(R.string.product_type_booking, "booking"),
OTHER;

fun isVariableProduct() = this == VARIABLE || this == VARIABLE_SUBSCRIPTION
Expand All @@ -34,6 +35,7 @@ enum class ProductType(@StringRes val stringResource: Int = 0, val value: String
"bundle" -> BUNDLE
"composite" -> COMPOSITE
"variation" -> VARIATION
"booking" -> BOOKING
else -> OTHER
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ import com.woocommerce.android.util.ChromeCustomTabUtils
import com.woocommerce.android.util.UiHelpers.getTextOfUiString
import com.woocommerce.android.util.WooAnimUtils
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.LaunchUrlInChromeTab
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowUiStringSnackbar
import com.woocommerce.android.widgets.CustomProgressDialog
import com.woocommerce.android.widgets.SkeletonView
Expand Down Expand Up @@ -402,9 +401,14 @@ class ProductDetailFragment :
private fun observeEvents(viewModel: ProductDetailViewModel) {
viewModel.event.observe(viewLifecycleOwner) { event ->
when (event) {
is LaunchUrlInChromeTab -> ChromeCustomTabUtils.launchUrl(requireContext(), event.url)
is Event.LaunchUrlInChromeTab -> ChromeCustomTabUtils.launchUrl(requireContext(), event.url)
is Event.LaunchUrlInAuthenticatedWebView -> findNavController(R.id.nav_host_fragment_main)
.navigateSafely(NavGraphMainDirections.actionGlobalAuthenticatedWebViewFragment(event.url))
is ProductDetailViewModel.OpenProductInWebView -> findNavController().navigateSafely(
ProductDetailFragmentDirections.actionProductDetailFragmentToProductDetailWebViewFragment(
remoteProductId = event.productId
)
)

is RefreshMenu -> toolbarHelper.setupToolbar()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.woocommerce.android.extensions.clearList
import com.woocommerce.android.extensions.containsItem
import com.woocommerce.android.extensions.fastStripHtml
import com.woocommerce.android.extensions.getList
import com.woocommerce.android.extensions.isCIABSite
import com.woocommerce.android.extensions.isEligibleForAI
import com.woocommerce.android.extensions.isEmpty
import com.woocommerce.android.extensions.isSitePublic
Expand Down Expand Up @@ -114,6 +115,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onCompletion
Expand Down Expand Up @@ -403,6 +405,7 @@ class ProductDetailViewModel @Inject constructor(

init {
start()
openInWebViewIfNeeded()
}

fun start() {
Expand All @@ -424,6 +427,17 @@ class ProductDetailViewModel @Inject constructor(
observeProductCategorySearchQuery()
}

fun openInWebViewIfNeeded() {
if (navArgs.mode !is ProductDetailFragment.Mode.ShowProduct) return

launch {
val product = storedProductAggregate.filterNotNull().first().product
if (selectedSite.get().isCIABSite() && product.productType == ProductType.BOOKING) {
triggerEvent(OpenProductInWebView(product.remoteId))
}
}
}

private fun initializeViewState() {
when (val mode = navArgs.mode) {
is ProductDetailFragment.Mode.AddNewProduct -> startAddNewProduct()
Expand Down Expand Up @@ -2726,6 +2740,8 @@ class ProductDetailViewModel @Inject constructor(

data class TrashProduct(val productId: Long) : Event()

data class OpenProductInWebView(val productId: Long) : Event()

/**
* [productDraft] is used for the UI. Any updates to the fields in the UI would update this model.
* [storedProductAggregate.value] is the [Product] model that is fetched from the API and available in the local db.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.woocommerce.android.ui.products.details.webview

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import com.woocommerce.android.R
import com.woocommerce.android.extensions.findNavController
import com.woocommerce.android.extensions.isTwoPanesShouldBeUsed
import com.woocommerce.android.ui.base.BaseFragment
import com.woocommerce.android.ui.compose.composeView
import com.woocommerce.android.ui.main.AppBarStatus
import com.woocommerce.android.viewmodel.MultiLiveEvent
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class ProductDetailWebViewFragment : BaseFragment() {
override val activityAppBarStatus: AppBarStatus
get() = AppBarStatus.Hidden

private val viewModel: ProductDetailWebViewViewModel by viewModels()
private val isInDetailPane: Boolean
get() = requireContext().isTwoPanesShouldBeUsed && parentFragment?.id == R.id.detail_nav_container

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return composeView {
ProductDetailWebViewScreen(
viewModel = viewModel,
showNavigationIcon = shouldShowNavigationIcon()
)
}
}

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

private fun handleEvents() {
viewModel.event.observe(viewLifecycleOwner) { event ->
when (event) {
is MultiLiveEvent.Event.Exit -> navigateBack()
}
}
}

private fun navigateBack() {
if (isInDetailPane) {
// If we are in the detail pane of a two-pane layout, we need to handle the back navigation
// in the Activity's main nav controller and not the detail pane's nav controller.
findNavController(R.id.nav_host_fragment_main).navigateUp()
} else {
findNavController().popBackStack(R.id.productDetailFragment, true)
}
}

private fun shouldShowNavigationIcon(): Boolean = !isInDetailPane
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.woocommerce.android.ui.products.details.webview

import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import com.woocommerce.android.ui.common.webview.WebViewAuthenticator
import com.woocommerce.android.ui.compose.component.Toolbar
import com.woocommerce.android.ui.compose.component.web.WCWebView
import org.wordpress.android.fluxc.network.UserAgent

@Composable
fun ProductDetailWebViewScreen(
viewModel: ProductDetailWebViewViewModel,
showNavigationIcon: Boolean
) {
viewModel.viewState.observeAsState().value?.let {
ProductDetailWebViewScreen(
viewState = it,
showNavigationIcon = showNavigationIcon,
webViewAuthenticator = viewModel.webViewAuthenticator,
userAgent = viewModel.userAgent,
)
}
}

@Composable
private fun ProductDetailWebViewScreen(
viewState: ProductDetailWebViewViewModel.ViewState,
showNavigationIcon: Boolean,
webViewAuthenticator: WebViewAuthenticator,
userAgent: UserAgent
) {
BackHandler(onBack = viewState.onBackClick)

Scaffold(
topBar = {
Toolbar(
title = viewState.title,
navigationIcon = if (showNavigationIcon) Icons.AutoMirrored.Default.ArrowBack else null,
onNavigationButtonClick = viewState.onBackClick
)
}
) {
WCWebView(
url = viewState.urlToLoad,
authenticator = webViewAuthenticator,
userAgent = userAgent,
modifier = Modifier
.padding(it)
.fillMaxSize()
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.woocommerce.android.ui.products.details.webview

import androidx.lifecycle.LiveData
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.liveData
import com.woocommerce.android.extensions.adminUrlOrDefault
import com.woocommerce.android.model.Product
import com.woocommerce.android.tools.SelectedSite
import com.woocommerce.android.ui.common.webview.WebViewAuthenticator
import com.woocommerce.android.ui.products.details.ProductDetailRepository
import com.woocommerce.android.viewmodel.MultiLiveEvent
import com.woocommerce.android.viewmodel.ScopedViewModel
import com.woocommerce.android.viewmodel.navArgs
import dagger.hilt.android.lifecycle.HiltViewModel
import org.wordpress.android.fluxc.network.UserAgent
import org.wordpress.android.fluxc.utils.extensions.slashJoin
import javax.inject.Inject

@HiltViewModel
class ProductDetailWebViewViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val productDetailRepository: ProductDetailRepository,
private val selectedSite: SelectedSite,
val webViewAuthenticator: WebViewAuthenticator,
val userAgent: UserAgent
) : ScopedViewModel(savedStateHandle) {
private val navArgs by savedStateHandle.navArgs<ProductDetailWebViewFragmentArgs>()

val viewState: LiveData<ViewState> = liveData {
val product = productDetailRepository.getProductAsync(navArgs.remoteProductId) ?: run {
navigateBack()
return@liveData
}

val urlToLoad = buildProductUrl(product)
emit(
ViewState(
urlToLoad = urlToLoad,
title = product.name,
onBackClick = ::navigateBack
)
)
}

private fun buildProductUrl(product: Product): String {
val site = selectedSite.get()
return site.adminUrlOrDefault.slashJoin("?page=next-admin&p=/woocommerce/products/edit/${product.remoteId}")
}

private fun navigateBack() {
triggerEvent(MultiLiveEvent.Event.Exit)
}

data class ViewState(
val urlToLoad: String,
val title: String,
val onBackClick: () -> Unit
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -669,13 +669,7 @@ class ProductListFragment :
private fun onProductClick(remoteProductId: Long, sharedView: View?) {
if (shouldPreventDetailNavigation(remoteProductId)) return
productListToolbar.disableSearchListeners()
(activity as? MainNavigationRouter)?.let { router ->
if (sharedView == null) {
router.showProductDetail(remoteProductId)
} else {
router.showProductDetailWithSharedTransition(remoteProductId, sharedView)
}
}
(activity as? MainNavigationRouter)?.showProductDetail(remoteProductId, sharedView = sharedView)
}

private fun showAddProductBottomSheet() {
Expand Down
Loading