Skip to content

Commit

Permalink
Add timer
Browse files Browse the repository at this point in the history
  • Loading branch information
ivan-magda committed Nov 18, 2023
1 parent f71293c commit 468f33e
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package org.hyperskill.app.challenges.widget.presentation

import kotlin.time.DurationUnit
import kotlin.time.toDuration
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.hyperskill.app.analytic.domain.interactor.AnalyticInteractor
Expand Down Expand Up @@ -29,6 +34,11 @@ class ChallengeWidgetActionDispatcher(
private val sentryInteractor: SentryInteractor,
private val analyticInteractor: AnalyticInteractor
) : CoroutineActionDispatcher<Action, Message>(config.createConfig()) {
private var timerJob: Job? = null

companion object {
private val TIMER_TICK_INTERVAL = 1.toDuration(DurationUnit.MINUTES)
}

init {
solvedStepsSharedFlow
Expand Down Expand Up @@ -61,6 +71,10 @@ class ChallengeWidgetActionDispatcher(
handleCreateMagicLinkAction(action, ::onNewMessage)
is InternalAction.LogAnalyticEvent ->
analyticInteractor.logEvent(action.analyticEvent)
InternalAction.LaunchTimer ->
handleLaunchTimerAction(::onNewMessage)
InternalAction.StopTimer ->
handleStopTimerAction()
else -> {
// no op
}
Expand Down Expand Up @@ -94,4 +108,24 @@ class ChallengeWidgetActionDispatcher(
}
)
}

private fun handleLaunchTimerAction(onNewMessage: (Message) -> Unit) {
if (timerJob != null) {
return
}

timerJob = flow {
while (true) {
delay(TIMER_TICK_INTERVAL)
emit(Unit)
}
}.onEach {
onNewMessage(InternalMessage.TimerTick)
}.launchIn(actionScope)
}

private fun handleStopTimerAction() {
timerJob?.cancel()
timerJob = null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ object ChallengeWidgetFeature {
object CreateMagicLinkError : InternalMessage
data class CreateMagicLinkSuccess(val url: String) : InternalMessage

object TimerTick : InternalMessage

// Observe target types changes
object StepSolved : InternalMessage
object DailyStepCompleted : InternalMessage
Expand All @@ -88,6 +90,9 @@ object ChallengeWidgetFeature {
internal sealed interface InternalAction : Action {
object FetchChallenges : InternalAction

object LaunchTimer : InternalAction
object StopTimer : InternalAction

data class CreateMagicLink(val nextUrl: String) : InternalAction

data class LogAnalyticEvent(val analyticEvent: AnalyticEvent) : InternalAction
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.hyperskill.app.challenges.widget.presentation

import org.hyperskill.app.challenges.domain.model.ChallengeStatus
import org.hyperskill.app.challenges.domain.model.ChallengeTargetType
import org.hyperskill.app.challenges.widget.domain.analytic.ChallengeWidgetClickedCollectRewardHyperskillAnalyticEvent
import org.hyperskill.app.challenges.widget.domain.analytic.ChallengeWidgetClickedDeadlineReloadHyperskillAnalyticEvent
Expand All @@ -22,7 +23,7 @@ class ChallengeWidgetReducer : StateReducer<State, Message, Action> {
InternalMessage.FetchChallengesError ->
State.Error to emptySet()
is InternalMessage.FetchChallengesSuccess ->
State.Content(challenges = message.challenges) to emptySet()
handleFetchChallengesSuccessMessage(message)
Message.RetryContentLoading ->
handleRetryContentLoadingMessage(state)
InternalMessage.PullToRefresh ->
Expand All @@ -43,6 +44,8 @@ class ChallengeWidgetReducer : StateReducer<State, Message, Action> {
handleDailyStepCompletedMessage(state)
InternalMessage.TopicCompleted ->
handleTopicCompletedMessage(state)
InternalMessage.TimerTick ->
handleTimerTickMessage(state)
} ?: (state to emptySet())

private fun handleInitializeMessage(
Expand All @@ -69,6 +72,22 @@ class ChallengeWidgetReducer : StateReducer<State, Message, Action> {
is State.Loading -> null
}

private fun handleFetchChallengesSuccessMessage(
message: InternalMessage.FetchChallengesSuccess
): ChallengeWidgetReducerResult {
val newState = State.Content(challenges = message.challenges)

val actions = when (newState.getCurrentChallenge()?.status) {
ChallengeStatus.NOT_STARTED,
ChallengeStatus.STARTED ->
setOf(InternalAction.LaunchTimer)
else ->
emptySet()
}

return newState to actions
}

private fun handleRetryContentLoadingMessage(state: State): ChallengeWidgetReducerResult? =
if (state is State.Error) {
State.Loading(isLoadingSilently = false) to setOf(
Expand Down Expand Up @@ -210,4 +229,24 @@ class ChallengeWidgetReducer : StateReducer<State, Message, Action> {
} else {
null
}

private fun handleTimerTickMessage(state: State): ChallengeWidgetReducerResult =
when (state) {
State.Idle,
State.Error,
is State.Loading ->
state to setOf(InternalAction.StopTimer)
is State.Content -> {
when (state.getCurrentChallenge()?.status) {
null,
ChallengeStatus.COMPLETED,
ChallengeStatus.PARTIAL_COMPLETED,
ChallengeStatus.NOT_COMPLETED ->
state to setOf(InternalAction.StopTimer)
ChallengeStatus.NOT_STARTED,
ChallengeStatus.STARTED ->
state to emptySet()
}
}
}
}

0 comments on commit 468f33e

Please sign in to comment.