diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mlp/ButtonsUiState.kt b/WordPress/src/main/java/org/wordpress/android/ui/mlp/ButtonsUiState.kt index 9688167ddb58..422096058944 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mlp/ButtonsUiState.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mlp/ButtonsUiState.kt @@ -4,7 +4,8 @@ package org.wordpress.android.ui.mlp * The buttons visibility state */ data class ButtonsUiState( - val createBlankPageVisible: Boolean, - val createPageVisible: Boolean, - val previewVisible: Boolean + val createBlankPageVisible: Boolean = true, + val createPageVisible: Boolean = false, + val previewVisible: Boolean = false, + val retryVisible: Boolean = false ) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mlp/GutenbergPageLayouts.kt b/WordPress/src/main/java/org/wordpress/android/ui/mlp/GutenbergPageLayouts.kt index e882debc92d6..5141b560f8f8 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mlp/GutenbergPageLayouts.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mlp/GutenbergPageLayouts.kt @@ -10,8 +10,8 @@ data class GutenbergPageLayouts( val layouts: List = listOf(), val categories: List = listOf() ) : Parcelable { - val isNotEmpty: Boolean - get() = layouts.isNotEmpty() || categories.isNotEmpty() + val isEmpty: Boolean + get() = layouts.isEmpty() || categories.isEmpty() fun getFilteredLayouts(categorySlug: String) = layouts.filter { l -> l.categories.any { c -> c.slug == categorySlug } } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mlp/ModalLayoutPickerFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/mlp/ModalLayoutPickerFragment.kt index 843ea71df642..343479f57f35 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mlp/ModalLayoutPickerFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mlp/ModalLayoutPickerFragment.kt @@ -21,8 +21,10 @@ import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment import kotlinx.android.synthetic.main.modal_layout_picker_bottom_toolbar.* import kotlinx.android.synthetic.main.modal_layout_picker_categories_skeleton.* +import kotlinx.android.synthetic.main.modal_layout_picker_error.* import kotlinx.android.synthetic.main.modal_layout_picker_fragment.* import kotlinx.android.synthetic.main.modal_layout_picker_layouts_skeleton.* +import kotlinx.android.synthetic.main.modal_layout_picker_subtitle_row.* import kotlinx.android.synthetic.main.modal_layout_picker_title_row.* import kotlinx.android.synthetic.main.modal_layout_picker_titlebar.* import org.wordpress.android.R @@ -33,8 +35,6 @@ import org.wordpress.android.ui.utils.UiHelpers import org.wordpress.android.util.AniUtils import org.wordpress.android.util.AniUtils.Duration import org.wordpress.android.util.DisplayUtils -import org.wordpress.android.util.ToastUtils -import org.wordpress.android.util.ToastUtils.Duration.SHORT import org.wordpress.android.util.setVisible import org.wordpress.android.viewmodel.mlp.ModalLayoutPickerViewModel import org.wordpress.android.viewmodel.mlp.ModalLayoutPickerViewModel.UiState.ContentUiState @@ -97,6 +97,9 @@ class ModalLayoutPickerFragment : BottomSheetDialogFragment() { previewButton.setOnClickListener { viewModel.onPreviewPageClicked() } + retryButton.setOnClickListener { + viewModel.onRetryClicked() + } setScrollListener() @@ -126,6 +129,14 @@ class ModalLayoutPickerFragment : BottomSheetDialogFragment() { } } + /** + * Sets the header description visibility + * @param visible if true the description is visible else invisible + */ + private fun setDescriptionVisibility(visible: Boolean) { + description?.visibility = if (visible) View.VISIBLE else View.INVISIBLE + } + override fun onCreateDialog(savedInstanceState: Bundle?) = BottomSheetDialog(requireContext(), getTheme()).apply { fillTheScreen(this) window?.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) @@ -171,22 +182,20 @@ class ModalLayoutPickerFragment : BottomSheetDialogFragment() { loadSavedState(savedInstanceState) viewModel.uiState.observe(this, Observer { uiState -> + setTitleVisibility(uiState.isHeaderVisible) + setDescriptionVisibility(uiState.isDescriptionVisible) + setButtonsVisibility(uiState.buttonsUiState) + setContentVisibility(uiState.loadingSkeletonVisible, uiState.errorViewVisible) when (uiState) { is LoadingUiState -> { - setTitleVisibility(uiState.isHeaderVisible) - showLoadingSkeleton(uiState.loadingSkeletonVisible) } is ContentUiState -> { (categoriesRecyclerView.adapter as CategoriesAdapter).setData(uiState.categories) (layoutsRecyclerView?.adapter as? LayoutCategoryAdapter)?.update(uiState.layoutCategories) - setButtonsVisibility(uiState.buttonsUiState) - setTitleVisibility(uiState.isHeaderVisible) - showLoadingSkeleton(uiState.loadingSkeletonVisible) } is ErrorUiState -> { - setTitleVisibility(uiState.isHeaderVisible) - showLoadingSkeleton(uiState.loadingSkeletonVisible) - ToastUtils.showToast(activity, uiState.message, SHORT) + actionableEmptyView.title.setText(uiState.title) + actionableEmptyView.subtitle.setText(uiState.subtitle) } } }) @@ -202,17 +211,20 @@ class ModalLayoutPickerFragment : BottomSheetDialogFragment() { }) } - private fun showLoadingSkeleton(skeleton: Boolean) { + private fun setContentVisibility(skeleton: Boolean, error: Boolean) { categoriesSkeleton.setVisible(skeleton) - categoriesRecyclerView.setVisible(!skeleton) + categoriesRecyclerView.setVisible(!skeleton && !error) layoutsSkeleton.setVisible(skeleton) - layoutsRecyclerView.setVisible(!skeleton) + layoutsRecyclerView.setVisible(!skeleton && !error) + errorLayout.setVisible(error) } private fun setButtonsVisibility(uiState: ButtonsUiState) { createBlankPageButton.setVisible(uiState.createBlankPageVisible) createPageButton.setVisible(uiState.createPageVisible) previewButton.setVisible(uiState.previewVisible) + retryButton.setVisible(uiState.retryVisible) + createOrRetryContainer.setVisible(uiState.createBlankPageVisible || uiState.retryVisible) } private fun fillTheScreen(dialog: BottomSheetDialog) { diff --git a/WordPress/src/main/java/org/wordpress/android/viewmodel/mlp/ModalLayoutPickerViewModel.kt b/WordPress/src/main/java/org/wordpress/android/viewmodel/mlp/ModalLayoutPickerViewModel.kt index 0b4c416a32c1..065ad52c0fb9 100644 --- a/WordPress/src/main/java/org/wordpress/android/viewmodel/mlp/ModalLayoutPickerViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/viewmodel/mlp/ModalLayoutPickerViewModel.kt @@ -107,11 +107,7 @@ class ModalLayoutPickerViewModel @Inject constructor( @Subscribe(threadMode = ThreadMode.MAIN) fun onBlockLayoutsFetched(event: OnBlockLayoutsFetched) { if (event.isError) { - if (networkUtils.isNetworkAvailable()) { - updateUiState(ErrorUiState(string.mlp_generic_error)) - } else { - updateUiState(ErrorUiState(string.no_network_message)) - } + setErrorState() } else { handleBlockLayoutsResponse(GutenbergPageLayouts(event.layouts, event.categories)) } @@ -124,6 +120,14 @@ class ModalLayoutPickerViewModel @Inject constructor( loadCategories() } + private fun setErrorState() { + if (networkUtils.isNetworkAvailable()) { + updateUiState(ErrorUiState(string.mlp_error_title, string.mlp_error_subtitle)) + } else { + updateUiState(ErrorUiState(string.mlp_network_error_title, string.mlp_network_error_subtitle)) + } + } + private fun loadLayouts() { val state = uiState.value as? ContentUiState ?: ContentUiState() launch(bgDispatcher) { @@ -183,6 +187,15 @@ class ModalLayoutPickerViewModel @Inject constructor( fetchLayouts() } + /** + * Retries data fetching + */ + fun onRetryClicked() { + if (networkUtils.isNetworkAvailable()) { + fetchLayouts() + } + } + /** * Dismisses the MLP */ @@ -308,11 +321,12 @@ class ModalLayoutPickerViewModel @Inject constructor( } fun loadSavedState(layouts: GutenbergPageLayouts?, selectedLayout: String?, selectedCategories: List?) { - if (layouts == null) { + if (layouts == null || layouts.isEmpty) { + setErrorState() return } - val categories = ArrayList(selectedCategories ?: listOf()) val state = uiState.value as? ContentUiState ?: ContentUiState() + val categories = ArrayList(selectedCategories ?: listOf()) updateUiState(state.copy(selectedLayoutSlug = selectedLayout, selectedCategoriesSlugs = categories)) updateButtonsUiState() handleBlockLayoutsResponse(layouts) @@ -320,8 +334,10 @@ class ModalLayoutPickerViewModel @Inject constructor( sealed class UiState( open val isHeaderVisible: Boolean = false, + val isDescriptionVisible: Boolean = true, val loadingSkeletonVisible: Boolean = false, - open val errorViewVisible: Boolean = false + val errorViewVisible: Boolean = false, + open val buttonsUiState: ButtonsUiState = ButtonsUiState() ) { object LoadingUiState : UiState(loadingSkeletonVisible = true) @@ -332,13 +348,14 @@ class ModalLayoutPickerViewModel @Inject constructor( val loadedThumbnailSlugs: ArrayList = arrayListOf(), val categories: List = listOf(), val layoutCategories: List = listOf(), - val buttonsUiState: ButtonsUiState = ButtonsUiState( - createBlankPageVisible = true, - previewVisible = false, - createPageVisible = false - ) + override val buttonsUiState: ButtonsUiState = ButtonsUiState() ) : UiState() - data class ErrorUiState(@StringRes val message: Int) : UiState(errorViewVisible = true) + data class ErrorUiState(@StringRes val title: Int, @StringRes val subtitle: Int) : UiState( + errorViewVisible = true, + isHeaderVisible = true, + isDescriptionVisible = false, + buttonsUiState = ButtonsUiState(retryVisible = true) + ) } } diff --git a/WordPress/src/main/res/layout/modal_layout_picker_bottom_toolbar.xml b/WordPress/src/main/res/layout/modal_layout_picker_bottom_toolbar.xml index dc4c4c375dfc..bf86cc8b7977 100644 --- a/WordPress/src/main/res/layout/modal_layout_picker_bottom_toolbar.xml +++ b/WordPress/src/main/res/layout/modal_layout_picker_bottom_toolbar.xml @@ -8,15 +8,32 @@ android:paddingBottom="@dimen/mlp_bottom_button_vertical_margin" android:paddingTop="@dimen/mlp_bottom_button_vertical_margin"> - + android:orientation="vertical" + android:visibility="gone"> + + + + + + + + + diff --git a/WordPress/src/main/res/layout/modal_layout_picker_fragment.xml b/WordPress/src/main/res/layout/modal_layout_picker_fragment.xml index 24787cd1ebb2..0b051ed38dac 100644 --- a/WordPress/src/main/res/layout/modal_layout_picker_fragment.xml +++ b/WordPress/src/main/res/layout/modal_layout_picker_fragment.xml @@ -96,6 +96,12 @@ android:scrollbars="vertical" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> + + diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index b3ee27ab5346..51b0224a6ca4 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -2898,7 +2898,10 @@ %s Page created Blank page created - An error occurred while fetching the layouts + Layouts not available due to an error + Tap retry or create a blank page using the button below. + Layouts not available while offline + Tap retry when you\'re back online or create a blank page using the button below. Capture Flip camera