Skip to content

Commit

Permalink
Shared, Android skip suggestion (#1126)
Browse files Browse the repository at this point in the history
  • Loading branch information
XanderZhu authored Jul 30, 2024
1 parent 35e59b8 commit 702ad41
Show file tree
Hide file tree
Showing 43 changed files with 1,214 additions and 267 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,11 @@ fun NestedScrollView.smoothScrollToBottom(durationMilliseconds: Int = DEFAULT_SM
val y = view.bottom + lp.bottomMargin + scrollView.paddingBottom
scrollView.smoothScrollTo(0, y, durationMilliseconds)
}
}

fun NestedScrollView.smoothScrollTo(view: View, durationMilliseconds: Int = DEFAULT_SMOOTH_SCROLL_DURATION) {
val scrollView = this
val lp = view.layoutParams as ViewGroup.MarginLayoutParams
val y = view.bottom + lp.bottomMargin
scrollView.smoothScrollTo(0, y, durationMilliseconds)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import org.hyperskill.app.android.HyperskillApp
import org.hyperskill.app.android.R
import org.hyperskill.app.android.core.extensions.argument
import org.hyperskill.app.android.core.extensions.logger
import org.hyperskill.app.android.core.extensions.smoothScrollTo
import org.hyperskill.app.android.core.extensions.smoothScrollToBottom
import org.hyperskill.app.android.core.view.ui.fragment.setChildFragment
import org.hyperskill.app.android.core.view.ui.navigation.requireRouter
Expand Down Expand Up @@ -228,4 +229,10 @@ class StageStepWrapperFragment :
.stagePracticeContainer
.smoothScrollToBottom(SMOOTH_SCROLL_DURATION_MILLISECONDS)
}

override fun scrollTo(view: View) {
viewBinding
.stagePracticeContainer
.smoothScrollTo(view, SMOOTH_SCROLL_DURATION_MILLISECONDS)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import org.hyperskill.app.step.domain.model.StepMenuSecondaryAction
@OptIn(FlowPreview::class)
class StepMenuDelegate(
menuHost: MenuHost,
private val viewLifecycleOwner: LifecycleOwner,
viewLifecycleOwner: LifecycleOwner,
private val onPrimaryActionClick: (StepMenuPrimaryAction) -> Unit,
private val onSecondaryActionClick: (StepMenuSecondaryAction) -> Unit,
private val onBackClick: () -> Unit
Expand Down Expand Up @@ -89,7 +89,6 @@ class StepMenuDelegate(
val menuItem: MenuItem? = menu.findItem(
when (action) {
StepMenuPrimaryAction.THEORY -> R.id.theory
StepMenuPrimaryAction.COMMENTS -> R.id.comments
}
)
val isVisible = params?.isVisible == true
Expand All @@ -108,21 +107,22 @@ class StepMenuDelegate(
StepMenuSecondaryAction.REPORT -> R.id.practiceFeedback
StepMenuSecondaryAction.SKIP -> R.id.skip
StepMenuSecondaryAction.OPEN_IN_WEB -> R.id.open_in_web
StepMenuSecondaryAction.COMMENTS -> R.id.comments
}
menu.findItem(menuItemId)?.isVisible = actions.contains(action)
}
}

override fun onMenuItemSelected(menuItem: MenuItem): Boolean =
when (menuItem.itemId) {
R.id.comments -> {
onPrimaryActionClick.invoke(StepMenuPrimaryAction.COMMENTS)
true
}
R.id.theory -> {
onPrimaryActionClick.invoke(StepMenuPrimaryAction.THEORY)
true
}
R.id.comments -> {
onSecondaryActionClick.invoke(StepMenuSecondaryAction.COMMENTS)
true
}
R.id.practiceFeedback -> {
onSecondaryActionClick.invoke(StepMenuSecondaryAction.REPORT)
true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import org.hyperskill.app.android.step.view.model.LimitsWidgetCallback
import org.hyperskill.app.android.step.view.model.StepCompletionHost
import org.hyperskill.app.android.step.view.model.StepCompletionView
import org.hyperskill.app.android.step.view.model.StepMenuPrimaryAction
import org.hyperskill.app.android.step.view.model.StepMenuPrimaryActionParams
import org.hyperskill.app.android.step.view.model.StepPracticeCallback
import org.hyperskill.app.android.step.view.model.StepToolbarCallback
import org.hyperskill.app.android.step.view.model.StepToolbarHost
Expand Down Expand Up @@ -136,13 +135,6 @@ class StepWrapperFragment :
initStepContainer(stepState)
parentOfType(StepToolbarHost::class.java)?.apply {
renderTopicProgress(state.stepToolbarViewState)
renderPrimaryAction(
StepMenuPrimaryAction.COMMENTS,
StepMenuPrimaryActionParams(
isVisible = state.isCommentsToolbarItemAvailable,
isEnabled = true
)
)
renderSecondaryMenuActions(state.stepMenuSecondaryActions)
}
(childFragmentManager.findFragmentByTag(STEP_CONTENT_TAG) as? StepCompletionView)
Expand Down Expand Up @@ -223,13 +215,18 @@ class StepWrapperFragment :
(childFragmentManager.findFragmentByTag(STEP_CONTENT_TAG) as? StepPracticeCallback)
?.onTheoryClick()
}
StepMenuPrimaryAction.COMMENTS -> {
stepViewModel.onCommentsClick()
}
}
}

override fun onSecondaryActionClicked(action: StepMenuSecondaryAction) {
stepViewModel.onActionClick(action)
}

override fun requestShowComments() {
stepViewModel.requestShowComments()
}

override fun requestSkip() {
stepViewModel.requestSkip()
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.hyperskill.app.android.step.view.model

enum class StepMenuPrimaryAction {
THEORY,
COMMENTS
THEORY
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ import org.hyperskill.app.step.domain.model.StepMenuSecondaryAction
interface StepToolbarCallback {
fun onPrimaryActionClicked(action: StepMenuPrimaryAction)
fun onSecondaryActionClicked(action: StepMenuSecondaryAction)
fun requestShowComments()
fun requestSkip()
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.hyperskill.app.android.step_practice.model

import android.view.View

interface StepPracticeHost {
fun fullScrollDown()
fun scrollTo(view: View)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import by.kirich1409.viewbindingdelegate.viewBinding
import dev.chrisbanes.insetter.applyInsetter
import org.hyperskill.app.android.R
import org.hyperskill.app.android.core.extensions.argument
import org.hyperskill.app.android.core.extensions.smoothScrollTo
import org.hyperskill.app.android.core.extensions.smoothScrollToBottom
import org.hyperskill.app.android.core.view.ui.fragment.setChildFragment
import org.hyperskill.app.android.databinding.FragmentStepPracticeBinding
Expand Down Expand Up @@ -90,4 +91,10 @@ class StepPracticeFragment :
.stepPracticeContainer
.smoothScrollToBottom(SMOOTH_SCROLL_DURATION_MILLISECONDS)
}

override fun scrollTo(view: View) {
stepPracticeViewBinding
.stepPracticeContainer
.smoothScrollTo(view, SMOOTH_SCROLL_DURATION_MILLISECONDS)
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
package org.hyperskill.app.android.step_quiz.view.delegate

import android.content.Context
import android.graphics.Paint
import android.graphics.drawable.AnimationDrawable
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.isVisible
import org.hyperskill.app.android.R
import org.hyperskill.app.android.core.view.ui.widget.ProgressableWebViewClient
import org.hyperskill.app.android.databinding.LayoutStepQuizFeedbackBlockBinding
import org.hyperskill.app.android.step_quiz.view.model.StepQuizFeedbackState
import org.hyperskill.app.step_quiz.presentation.StepQuizFeature
import org.hyperskill.app.step_quiz.view.model.StepQuizFeedbackState
import org.hyperskill.app.step_quiz.view.model.StepQuizFeedbackState.Wrong.Action
import ru.nobird.android.view.base.ui.delegate.ViewStateDelegate
import ru.nobird.android.view.base.ui.extension.getDrawableCompat

class StepQuizFeedbackBlocksDelegate(
context: Context,
private val layoutStepQuizFeedbackBlockBinding: LayoutStepQuizFeedbackBlockBinding
private val layoutStepQuizFeedbackBlockBinding: LayoutStepQuizFeedbackBlockBinding,
private val onNewMessage: (StepQuizFeature.Message) -> Unit
) {
companion object {
private const val EVALUATION_FRAME_DURATION_MS = 250
private const val NON_LATEX_HINT_TEMPLATE = """<pre><span style="font-family: 'Roboto';">%s</span></pre>"""
}

private val viewStateDelegate = ViewStateDelegate<StepQuizFeedbackState>()

init {
with(viewStateDelegate) {
addState<StepQuizFeedbackState.Idle>()
addState<StepQuizFeedbackState.Unsupported>(
addState<StepQuizFeedbackState.UnsupportedStep>(
layoutStepQuizFeedbackBlockBinding.stepQuizFeedbackUnsupported
)
addState<StepQuizFeedbackState.Evaluation>(
Expand All @@ -38,7 +43,7 @@ class StepQuizFeedbackBlocksDelegate(
layoutStepQuizFeedbackBlockBinding.stepQuizFeedbackWrong,
layoutStepQuizFeedbackBlockBinding.stepQuizFeedback
)
addState<StepQuizFeedbackState.Validation>(
addState<StepQuizFeedbackState.ValidationFailed>(
layoutStepQuizFeedbackBlockBinding.stepQuizFeedbackValidation
)
addState<StepQuizFeedbackState.RejectedSubmission>(
Expand All @@ -58,19 +63,50 @@ class StepQuizFeedbackBlocksDelegate(
textView?.typeface =
ResourcesCompat.getFont(context, R.font.pt_mono)
}

with(layoutStepQuizFeedbackBlockBinding.stepQuizFeedbackWrongAction) {
paintFlags = paintFlags or Paint.UNDERLINE_TEXT_FLAG
}
}

fun setState(state: StepQuizFeedbackState) {
viewStateDelegate.switchState(state)
layoutStepQuizFeedbackBlockBinding.root.isVisible = state !is StepQuizFeedbackState.Idle
when (state) {
is StepQuizFeedbackState.Correct -> {
setHint(layoutStepQuizFeedbackBlockBinding, state.hint)
setHint(layoutStepQuizFeedbackBlockBinding, state.hint, state.useLatex)
}
is StepQuizFeedbackState.Wrong -> {
setHint(layoutStepQuizFeedbackBlockBinding, state.hint)
with(layoutStepQuizFeedbackBlockBinding) {
stepQuizFeedbackWrongTitle.text = state.title
stepQuizFeedbackWrongDescription.isVisible = state.description != null
if (state.description != null) {
stepQuizFeedbackWrongDescription.text = state.description
}

stepQuizFeedbackWrongAction.isVisible = state.actionText != null
if (state.actionText != null) {
stepQuizFeedbackWrongAction.text = state.actionText
}
val action = state.actionType
stepQuizFeedbackWrongAction.setOnClickListener(
if (action != null) {
{
onNewMessage(
when (action) {
Action.SEE_HINT -> StepQuizFeature.Message.SeeHintClicked
Action.READ_COMMENTS -> StepQuizFeature.Message.ReadCommentsClicked
Action.SKIP_PROBLEM -> StepQuizFeature.Message.SkipClicked
}
)
}
} else null
)

setHint(layoutStepQuizFeedbackBlockBinding, state.feedbackHint, state.useFeedbackHintLatex)
}
}
is StepQuizFeedbackState.Validation -> {
is StepQuizFeedbackState.ValidationFailed -> {
layoutStepQuizFeedbackBlockBinding.stepQuizFeedbackValidation.text = state.message
}
is StepQuizFeedbackState.RejectedSubmission -> {
Expand All @@ -84,10 +120,18 @@ class StepQuizFeedbackBlocksDelegate(

private fun setHint(
layoutStepQuizFeedbackBlockBinding: LayoutStepQuizFeedbackBlockBinding,
hint: String?
hint: String?,
useLatex: Boolean
) {
layoutStepQuizFeedbackBlockBinding.stepQuizFeedback.isVisible = hint != null
layoutStepQuizFeedbackBlockBinding.stepQuizFeedbackBody.setText(hint)
val resultHint = hint?.let {
if (useLatex) {
hint
} else {
String.format(NON_LATEX_HINT_TEMPLATE, hint)
}
}
layoutStepQuizFeedbackBlockBinding.stepQuizFeedback.isVisible = resultHint != null
layoutStepQuizFeedbackBlockBinding.stepQuizFeedbackBody.setText(resultHint)
}

private fun getEvaluationDrawable(context: Context): AnimationDrawable =
Expand Down
Loading

0 comments on commit 702ad41

Please sign in to comment.