Skip to content

Commit

Permalink
Merge pull request #16216 from wordpress-mobile/issue/16109-blogging-…
Browse files Browse the repository at this point in the history
…prompts-introduction-dialog-ui

Implement Blogging Prompts introduction dialog UI
  • Loading branch information
RenanLukas authored Apr 1, 2022
2 parents 558badc + 1dc2581 commit c8642fd
Show file tree
Hide file tree
Showing 18 changed files with 543 additions and 155 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.wordpress.android.ui.bloggingprompts.onboarding

import org.wordpress.android.models.bloggingprompts.BloggingPrompt

sealed class BloggingPromptsOnboardingAction {
data class OpenEditor(val bloggingPrompt: BloggingPrompt) : BloggingPromptsOnboardingAction()
object OpenEditor : BloggingPromptsOnboardingAction()

object OpenSitePicker : BloggingPromptsOnboardingAction()

object OpenRemindersIntro : BloggingPromptsOnboardingAction()
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
package org.wordpress.android.ui.bloggingprompts.onboarding

import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import androidx.core.text.bold
import androidx.core.text.buildSpannedString
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.button.MaterialButton
import org.wordpress.android.R
import org.wordpress.android.WordPress
import org.wordpress.android.databinding.BloggingPromptsOnboardingDialogFragmentBinding
import org.wordpress.android.databinding.BloggingPromptsOmboardingDialogContentViewBinding
import org.wordpress.android.ui.ActivityLauncher
import org.wordpress.android.ui.bloggingprompts.onboarding.BloggingPromptsOnboardingAction.OpenEditor
import org.wordpress.android.ui.bloggingprompts.onboarding.BloggingPromptsOnboardingAction.OpenRemindersIntro
import org.wordpress.android.ui.bloggingprompts.onboarding.BloggingPromptsOnboardingAction.OpenSitePicker
import org.wordpress.android.ui.bloggingprompts.onboarding.BloggingPromptsOnboardingUiState.Ready
import org.wordpress.android.ui.featureintroduction.FeatureIntroductionDialogFragment
import org.wordpress.android.util.extensions.exhaustive
import org.wordpress.android.util.extensions.setStatusBarAsSurfaceColor
import javax.inject.Inject

class BloggingPromptsOnboardingDialogFragment : DialogFragment() {
class BloggingPromptsOnboardingDialogFragment : FeatureIntroductionDialogFragment() {
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
private lateinit var viewModel: BloggingPromptsOnboardingViewModel

Expand All @@ -29,28 +29,14 @@ class BloggingPromptsOnboardingDialogFragment : DialogFragment() {
fun newInstance(): BloggingPromptsOnboardingDialogFragment = BloggingPromptsOnboardingDialogFragment()
}

override fun getTheme(): Int {
return R.style.BloggingPromptsOnboardingDialogFragment
}

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
viewModel = ViewModelProvider(this, viewModelFactory)
.get(BloggingPromptsOnboardingViewModel::class.java)
dialog.setStatusBarAsSurfaceColor()
return dialog
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View = BloggingPromptsOnboardingDialogFragmentBinding.inflate(inflater).root

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = BloggingPromptsOnboardingDialogFragmentBinding.bind(view)
setupTryNow(binding.tryNow)
viewModel = ViewModelProvider(this, viewModelFactory).get(BloggingPromptsOnboardingViewModel::class.java)
setupTryNowButton()
setupRemindMeButton()
setupHeaderTitle()
setupHeaderIcon()
setupUiStateObserver()
setupActionObserver()
viewModel.start()
}
Expand All @@ -60,15 +46,59 @@ class BloggingPromptsOnboardingDialogFragment : DialogFragment() {
(requireActivity().applicationContext as WordPress).component().inject(this)
}

private fun setupTryNow(tryNow: MaterialButton) {
tryNow.setOnClickListener { viewModel.onTryNow() }
private fun setupTryNowButton() {
setPrimaryButtonListener { viewModel.onTryNowClick() }
setPrimaryButtonText(R.string.blogging_prompts_onboarding_try_it_now)
}

private fun setupRemindMeButton() {
setSecondaryButtonListener { viewModel.onRemindMeClick() }
setSecondaryButtonText(R.string.blogging_prompts_onboarding_remind_me)
}

private fun setupHeaderTitle() {
setHeaderTitle(R.string.blogging_prompts_onboarding_header_title)
}

private fun setupHeaderIcon() {
setHeaderIcon(R.drawable.ic_outline_lightbulb_orange_gradient_40dp)
}

private fun setupContent(readyState: Ready) {
val contentBinding = BloggingPromptsOmboardingDialogContentViewBinding.inflate(layoutInflater)
setContent(contentBinding.root)
with(contentBinding) {
contentTop.text = getString(readyState.contentTopRes)
cardCoverView.setOnClickListener { /*do nothing*/ }
promptCard.promptContent.text = getString(readyState.promptRes)
promptCard.numberOfAnswers.text = getString(readyState.answersRes, readyState.answersCount)
contentBottom.text = getString(readyState.contentBottomRes)
contentNote.text = buildSpannedString {
bold { append("${getString(readyState.contentNoteTitle)} ") }
append(getString(readyState.contentNoteContent))
}
}
}

private fun setupUiStateObserver() {
viewModel.uiState.observe(this) { uiState ->
when (uiState) {
is Ready -> {
setupContent(uiState)
}
}.exhaustive
}
}

private fun setupActionObserver() {
viewModel.action.observe(this, { action ->
viewModel.action.observe(this) { action ->
when (action) {
is OpenEditor -> ActivityLauncher.openEditorInNewStack(activity)
is OpenSitePicker -> { /*TODO*/
}
is OpenRemindersIntro -> { /*TODO*/
}
}.exhaustive
})
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
package org.wordpress.android.ui.bloggingprompts.onboarding

sealed class BloggingPromptsOnboardingUiState
import androidx.annotation.StringRes

sealed class BloggingPromptsOnboardingUiState {
data class Ready(
@StringRes val promptRes: Int,
@StringRes val answersRes: Int,
val answersCount: Int,
@StringRes val contentTopRes: Int,
@StringRes val contentBottomRes: Int,
@StringRes val contentNoteTitle: Int,
@StringRes val contentNoteContent: Int
) : BloggingPromptsOnboardingUiState()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.wordpress.android.ui.bloggingprompts.onboarding

import org.wordpress.android.R
import org.wordpress.android.ui.bloggingprompts.onboarding.BloggingPromptsOnboardingUiState.Ready
import javax.inject.Inject

class BloggingPromptsOnboardingUiStateMapper @Inject constructor() {
fun mapReady(): Ready = Ready(
promptRes = R.string.blogging_prompts_onboarding_card_prompt,
answersRes = R.string.my_site_blogging_prompt_card_number_of_answers,
answersCount = ANSWER_COUNT,
contentTopRes = R.string.blogging_prompts_onboarding_content_top,
contentBottomRes = R.string.blogging_prompts_onboarding_content_bottom,
contentNoteTitle = R.string.blogging_prompts_onboarding_content_note_title,
contentNoteContent = R.string.blogging_prompts_onboarding_content_note_content
)
}

private const val ANSWER_COUNT = 19
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,36 @@ package org.wordpress.android.ui.bloggingprompts.onboarding
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.single
import kotlinx.coroutines.launch
import org.wordpress.android.models.bloggingprompts.BloggingPrompt
import org.wordpress.android.models.usecases.GetBloggingPromptUseCase
import org.wordpress.android.fluxc.store.SiteStore
import org.wordpress.android.ui.bloggingprompts.onboarding.BloggingPromptsOnboardingAction.OpenEditor
import org.wordpress.android.ui.bloggingprompts.onboarding.BloggingPromptsOnboardingAction.OpenRemindersIntro
import org.wordpress.android.ui.bloggingprompts.onboarding.BloggingPromptsOnboardingAction.OpenSitePicker
import javax.inject.Inject

class BloggingPromptsOnboardingViewModel @Inject constructor(
private val getBloggingPromptUseCase: GetBloggingPromptUseCase
private val siteStore: SiteStore,
private val uiStateMapper: BloggingPromptsOnboardingUiStateMapper
) : ViewModel() {
private lateinit var bloggingPrompt: BloggingPrompt

private val _uiState = MutableLiveData<BloggingPromptsOnboardingUiState>()
val uiState: LiveData<BloggingPromptsOnboardingUiState> = _uiState

private val _action = MutableLiveData<BloggingPromptsOnboardingAction>()
val action: LiveData<BloggingPromptsOnboardingAction> = _action

fun start() {
viewModelScope.launch {
bloggingPrompt = getBloggingPromptUseCase.execute().single()
}
_uiState.value = uiStateMapper.mapReady()
}

fun onTryNow() {
_action.value = OpenEditor(bloggingPrompt)
fun onTryNowClick() {
// TODO send BloggingPrompt with OpenEditor action when prompt store is ready
_action.value = OpenEditor
}

fun onRemindMeClick() {
if (siteStore.sitesCount > 1) {
_action.value = OpenSitePicker
} else {
_action.value = OpenRemindersIntro
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package org.wordpress.android.ui.featureintroduction

import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.DialogFragment
import org.wordpress.android.R
import org.wordpress.android.databinding.FeatureIntroductionDialogFragmentBinding
import org.wordpress.android.util.extensions.setStatusBarAsSurfaceColor

@Suppress("TooManyFunctions")
abstract class FeatureIntroductionDialogFragment : DialogFragment() {
private var _binding: FeatureIntroductionDialogFragmentBinding? = null
private val binding get() = _binding ?: throw NullPointerException("_binding cannot be null")

override fun getTheme(): Int {
return R.style.FeatureIntroductionDialogFragment
}

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.setStatusBarAsSurfaceColor()
return dialog
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View = FeatureIntroductionDialogFragmentBinding.inflate(inflater).root

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FeatureIntroductionDialogFragmentBinding.bind(view)
setupCloseButton()
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
}

fun setPrimaryButtonListener(listener: () -> Unit) {
binding.primaryButton.setOnClickListener { listener() }
}

fun setPrimaryButtonText(@StringRes textRes: Int) {
binding.primaryButton.text = getString(textRes)
}

fun setSecondaryButtonListener(listener: () -> Unit) {
binding.secondaryButton.setOnClickListener { listener() }
}

fun setSecondaryButtonText(@StringRes textRes: Int) {
binding.secondaryButton.text = getString(textRes)
}

fun setHeaderTitle(@StringRes headerTitleRes: Int) {
binding.headerTitle.text = getString(headerTitleRes)
}

fun setHeaderIcon(@DrawableRes headerIconRes: Int) {
binding.headerIcon.setImageDrawable(ResourcesCompat.getDrawable(resources, headerIconRes, context?.theme))
}

fun setContent(view: View) {
binding.contentContainer.addView(view)
}

private fun setupCloseButton() {
binding.closeButton.setOnClickListener { dismiss() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.view.View
import android.view.ViewTreeObserver
import android.view.inputmethod.InputMethodManager
import androidx.annotation.DimenRes
import androidx.annotation.StringRes
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator

Expand Down Expand Up @@ -80,3 +81,5 @@ fun View.focusAndShowKeyboard() {
fun RecyclerView.disableAnimation() {
(itemAnimator as? SimpleItemAnimator)?.supportsChangeAnimations = false
}

fun View.getString(@StringRes stringRes: Int) = context.getString(stringRes)
6 changes: 6 additions & 0 deletions WordPress/src/main/res/color/lightbulb_orange_background.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<gradient xmlns:android="http://schemas.android.com/apk/res/android"
android:angle="90"
android:startColor="#E25781"
android:endColor="#DEB100"
android:type="linear" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/default_cardview_radius" />
<stroke
android:width="@dimen/unelevated_card_stroke_width"
android:color="@color/on_surface_divider" />
</shape>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="40dp"
android:viewportHeight="24"
android:viewportWidth="24">

<path
android:fillColor="@color/lightbulb_orange_background"
android:pathData="M9,21c0,0.55 0.45,1 1,1h4c0.55,0 1,-0.45 1,-1v-1L9,20v1zM12,2C8.14,2 5,5.14 5,9c0,2.38 1.19,4.47 3,5.74L8,17c0,0.55 0.45,1 1,1h6c0.55,0 1,-0.45 1,-1v-2.26c1.81,-1.27 3,-3.36 3,-5.74 0,-3.86 -3.14,-7 -7,-7zM14.85,13.1l-0.85,0.6L14,16h-4v-2.3l-0.85,-0.6C7.8,12.16 7,10.63 7,9c0,-2.76 2.24,-5 5,-5s5,2.24 5,5c0,1.63 -0.8,3.16 -2.15,4.1z" />
</vector>
Loading

0 comments on commit c8642fd

Please sign in to comment.