Skip to content

Commit

Permalink
Shared, iOS: Improve track switching flow (#1110)
Browse files Browse the repository at this point in the history
^ALTAPPS-1290
  • Loading branch information
ivan-magda authored Jul 23, 2024
1 parent e4c6e91 commit 9940fa7
Show file tree
Hide file tree
Showing 29 changed files with 420 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ class StudyPlanFragment :
logger = logger
)
}
StudyPlanScreenFeature.Action.ViewAction.NavigateTo.TrackSelectionScreen -> {
// TODO: ALTAPPS-1291 Implement navigation to track selection screen
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ final class StudyPlanViewModel: FeatureViewModel<
onNewMessage(StudyPlanScreenFeatureMessagePullToRefresh())
}

func doTrackSelectionPresentation() {
onNewMessage(StudyPlanScreenFeatureMessageChangeTrackButtonClicked())
}

func doSectionToggle(sectionId: Int64) {
onNewMessage(
StudyPlanScreenFeatureMessageStudyPlanWidgetMessage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,17 @@ struct StudyPlanView: View {
ScrollView {
LazyVStack(alignment: .leading, spacing: LayoutInsets.defaultInset) {
if let trackTitle = viewModel.state.trackTitle {
Text(trackTitle)
.font(.subheadline)
.foregroundColor(.secondaryText)
.padding(.bottom, appearance.trackTitleBottomPadding)
Button(action: viewModel.doTrackSelectionPresentation) {
HStack {
Text(trackTitle)

Image(systemName: "arrow.left.arrow.right.square")
.imageScale(.large)
}
}
.font(.subheadline)
.foregroundColor(.secondaryText)
.padding(.bottom, appearance.trackTitleBottomPadding)
}

let usersInterviewWidgetFeatureStateKs = viewModel.usersInterviewWidgetFeatureStateKs
Expand Down Expand Up @@ -120,6 +127,8 @@ private extension StudyPlanView {
_ viewAction: StudyPlanScreenFeatureActionViewAction
) {
switch StudyPlanScreenFeatureActionViewActionKs(viewAction) {
case .navigateTo(let navigateToViewAction):
handleNavigateToViewAction(navigateToViewAction)
case .gamificationToolbarViewAction(let gamificationToolbarViewAction):
GamificationToolbarViewActionHandler.handle(
viewAction: gamificationToolbarViewAction.viewAction,
Expand All @@ -137,6 +146,16 @@ private extension StudyPlanView {
}
}

func handleNavigateToViewAction(
_ viewAction: StudyPlanScreenFeatureActionViewActionNavigateTo
) {
switch StudyPlanScreenFeatureActionViewActionNavigateToKs(viewAction) {
case .trackSelectionScreen:
let assembly = TrackSelectionListAssembly(isNewUserMode: false)
stackRouter.pushViewController(assembly.makeModule())
}
}

func handleStudyPlanWidgetViewAction(
_ viewAction: StudyPlanWidgetFeatureActionViewAction
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ import org.hyperskill.app.topics_repetitions.injection.TopicsRepetitionsDataComp
import org.hyperskill.app.topics_repetitions.injection.TopicsRepetitionsFlowDataComponent
import org.hyperskill.app.track.injection.TrackDataComponent
import org.hyperskill.app.track_selection.details.injection.TrackSelectionDetailsComponent
import org.hyperskill.app.track_selection.details.injection.TrackSelectionDetailsDataComponent
import org.hyperskill.app.track_selection.list.injection.TrackSelectionListComponent
import org.hyperskill.app.user_storage.injection.UserStorageComponent
import org.hyperskill.app.users_interview_widget.injection.UsersInterviewWidgetComponent
Expand Down Expand Up @@ -149,6 +150,7 @@ interface AppGraph {
fun buildTrackDataComponent(): TrackDataComponent
fun buildTrackSelectionListComponent(): TrackSelectionListComponent
fun buildTrackSelectionDetailsComponent(): TrackSelectionDetailsComponent
fun buildTrackSelectionDetailsDataComponent(): TrackSelectionDetailsDataComponent
fun buildProfileComponent(): ProfileComponent
fun buildProfileSettingsComponent(): ProfileSettingsComponent
fun buildHomeComponent(): HomeComponent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ import org.hyperskill.app.track.injection.TrackDataComponent
import org.hyperskill.app.track.injection.TrackDataComponentImpl
import org.hyperskill.app.track_selection.details.injection.TrackSelectionDetailsComponent
import org.hyperskill.app.track_selection.details.injection.TrackSelectionDetailsComponentImpl
import org.hyperskill.app.track_selection.details.injection.TrackSelectionDetailsDataComponent
import org.hyperskill.app.track_selection.details.injection.TrackSelectionDetailsDataComponentImpl
import org.hyperskill.app.track_selection.list.injection.TrackSelectionListComponent
import org.hyperskill.app.track_selection.list.injection.TrackSelectionListComponentImpl
import org.hyperskill.app.user_storage.injection.UserStorageComponent
Expand Down Expand Up @@ -402,6 +404,9 @@ abstract class BaseAppGraph : AppGraph {
override fun buildTrackSelectionDetailsComponent(): TrackSelectionDetailsComponent =
TrackSelectionDetailsComponentImpl(this)

override fun buildTrackSelectionDetailsDataComponent(): TrackSelectionDetailsDataComponent =
TrackSelectionDetailsDataComponentImpl(this)

override fun buildStudyPlanScreenComponent(): StudyPlanScreenComponent =
StudyPlanScreenComponentImpl(this)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,12 @@ object HyperskillSentryTransactionBuilder {
operation = HyperskillSentryTransactionOperation.API_LOAD
)

fun buildTrackSelectionDetailsScreenSelectTrack(): HyperskillSentryTransaction =
HyperskillSentryTransaction(
name = "track-selection-details-feature-select-track",
operation = HyperskillSentryTransactionOperation.API_LOAD
)

/**
* ProgressScreenFeature
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.hyperskill.app.study_plan.domain.analytic

import org.hyperskill.app.analytic.domain.model.hyperskill.HyperskillAnalyticAction
import org.hyperskill.app.analytic.domain.model.hyperskill.HyperskillAnalyticEvent
import org.hyperskill.app.analytic.domain.model.hyperskill.HyperskillAnalyticPart
import org.hyperskill.app.analytic.domain.model.hyperskill.HyperskillAnalyticRoute
import org.hyperskill.app.analytic.domain.model.hyperskill.HyperskillAnalyticTarget

/**
* Represents a click on "Change track" button analytic event.
*
* JSON payload:
* ```
* {
* "route": "/study-plan"",
* "action": "click",
* "part": "main",
* "target": "change_track"
* }
* ```
*
* @see HyperskillAnalyticEvent
*/
object StudyPlanClickedChangeTrackHyperskillAnalyticEvent : HyperskillAnalyticEvent(
route = HyperskillAnalyticRoute.StudyPlan(),
action = HyperskillAnalyticAction.CLICK,
part = HyperskillAnalyticPart.MAIN,
target = HyperskillAnalyticTarget.CHANGE_TRACK
)
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,17 @@ object StudyPlanScreenFeature {
)

sealed interface Message {
object Initialize : Message
data object Initialize : Message

object RetryContentLoading : Message
data object RetryContentLoading : Message

object PullToRefresh : Message
data object PullToRefresh : Message

object ScreenBecomesActive : Message
data object ScreenBecomesActive : Message

object ViewedEventMessage : Message
data object ChangeTrackButtonClicked : Message

data object ViewedEventMessage : Message

data class GamificationToolbarMessage(
val message: GamificationToolbarFeature.Message
Expand All @@ -52,6 +54,10 @@ object StudyPlanScreenFeature {

sealed interface Action {
sealed interface ViewAction : Action {
sealed interface NavigateTo : ViewAction {
data object TrackSelectionScreen : NavigateTo
}

data class GamificationToolbarViewAction(
val viewAction: GamificationToolbarFeature.Action.ViewAction
) : ViewAction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.hyperskill.app.study_plan.screen.presentation

import org.hyperskill.app.gamification_toolbar.presentation.GamificationToolbarFeature
import org.hyperskill.app.gamification_toolbar.presentation.GamificationToolbarReducer
import org.hyperskill.app.study_plan.domain.analytic.StudyPlanClickedChangeTrackHyperskillAnalyticEvent
import org.hyperskill.app.study_plan.domain.analytic.StudyPlanClickedPullToRefreshHyperskillAnalyticEvent
import org.hyperskill.app.study_plan.domain.analytic.StudyPlanClickedRetryContentLoadingHyperskillAnalyticEvent
import org.hyperskill.app.study_plan.domain.analytic.StudyPlanViewedHyperskillAnalyticEvent
Expand Down Expand Up @@ -58,6 +59,8 @@ internal class StudyPlanScreenReducer(
)
)
}
StudyPlanScreenFeature.Message.ChangeTrackButtonClicked ->
handleChangeTrackButtonClicked(state)
}

private fun initializeFeatures(
Expand Down Expand Up @@ -124,6 +127,14 @@ internal class StudyPlanScreenReducer(
)
}

private fun handleChangeTrackButtonClicked(
state: StudyPlanScreenFeature.State
): StudyPlanScreenReducerResult =
state to setOf(
StudyPlanScreenFeature.InternalAction.LogAnalyticEvent(StudyPlanClickedChangeTrackHyperskillAnalyticEvent),
StudyPlanScreenFeature.Action.ViewAction.NavigateTo.TrackSelectionScreen
)

private fun reduceToolbarMessage(
state: GamificationToolbarFeature.State,
message: GamificationToolbarFeature.Message
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.hyperskill.app.track_selection.details.cache

import com.russhwolf.settings.Settings
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import org.hyperskill.app.core.utils.mutate
import org.hyperskill.app.track_selection.details.data.source.TrackSelectionDetailsCacheDataSource

@Serializable
private class TrackSelectionDetailsCountContainer(val map: Map<Long, Int>)

internal class TrackSelectionDetailsCacheDataSourceImpl(
private val json: Json,
private val settings: Settings
) : TrackSelectionDetailsCacheDataSource {
override fun getTracksSelectionCountMap(): Map<Long, Int> =
settings
.getStringOrNull(TrackSelectionDetailsCacheKeyValues.TRACK_SELECTION_DETAILS_COUNT_MAP)
?.let { json.decodeFromString(TrackSelectionDetailsCountContainer.serializer(), it).map }
?: emptyMap()

override fun incrementTrackSelectionCount(trackId: Long) {
val updatedMap = getTracksSelectionCountMap().mutate {
val current = get(trackId) ?: 0
set(trackId, current + 1)
}
settings.putString(
TrackSelectionDetailsCacheKeyValues.TRACK_SELECTION_DETAILS_COUNT_MAP,
json.encodeToString(
TrackSelectionDetailsCountContainer.serializer(),
TrackSelectionDetailsCountContainer(updatedMap)
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.hyperskill.app.track_selection.details.cache

internal object TrackSelectionDetailsCacheKeyValues {
const val TRACK_SELECTION_DETAILS_COUNT_MAP = "track_selection_details_count_map"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.hyperskill.app.track_selection.details.data.repository

import org.hyperskill.app.track_selection.details.data.source.TrackSelectionDetailsCacheDataSource
import org.hyperskill.app.track_selection.details.domain.repository.TrackSelectionDetailsRepository

internal class TrackSelectionDetailsRepositoryImpl(
private val trackSelectionDetailsCacheDataSource: TrackSelectionDetailsCacheDataSource
) : TrackSelectionDetailsRepository {
override fun getTracksSelectionCountMap(): Map<Long, Int> =
trackSelectionDetailsCacheDataSource.getTracksSelectionCountMap()

override fun incrementTrackSelectionCount(trackId: Long) {
trackSelectionDetailsCacheDataSource.incrementTrackSelectionCount(trackId)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.hyperskill.app.track_selection.details.data.source

interface TrackSelectionDetailsCacheDataSource {
fun getTracksSelectionCountMap(): Map<Long, Int>
fun incrementTrackSelectionCount(trackId: Long)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.hyperskill.app.track_selection.details.domain.repository

interface TrackSelectionDetailsRepository {
fun getTracksSelectionCountMap(): Map<Long, Int>
fun incrementTrackSelectionCount(trackId: Long)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@ import org.hyperskill.app.track_selection.details.presentation.TrackSelectionDet
import org.hyperskill.app.track_selection.details.presentation.TrackSelectionDetailsFeature.ViewState
import ru.nobird.app.presentation.redux.feature.Feature

class TrackSelectionDetailsComponentImpl(
internal class TrackSelectionDetailsComponentImpl(
private val appGraph: AppGraph
) : TrackSelectionDetailsComponent {
override fun trackSelectionDetailsFeature(
trackSelectionDetailsParams: TrackSelectionDetailsParams
): Feature<ViewState, Message, Action> {
val profileComponent = appGraph.profileDataComponent
val trackSelectionDetailsDataComponent = appGraph.buildTrackSelectionDetailsDataComponent()
return TrackSelectionDetailsFeatureBuilder.build(
trackSelectionDetailsParams,
resourceProvider = appGraph.commonComponent.resourceProvider,
dateFormatter = appGraph.commonComponent.dateFormatter,
currentSubscriptionStateRepository = appGraph.stateRepositoriesComponent.currentSubscriptionStateRepository,
trackSelectionDetailsRepository = trackSelectionDetailsDataComponent.trackSelectionDetailsRepository,
providersRepository = appGraph.buildProvidersDataComponent().providersRepository,
sentryInteractor = appGraph.sentryComponent.sentryInteractor,
profileRepository = profileComponent.profileRepository,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.hyperskill.app.track_selection.details.injection

import org.hyperskill.app.track_selection.details.domain.repository.TrackSelectionDetailsRepository

interface TrackSelectionDetailsDataComponent {
val trackSelectionDetailsRepository: TrackSelectionDetailsRepository
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.hyperskill.app.track_selection.details.injection

import org.hyperskill.app.core.injection.AppGraph
import org.hyperskill.app.track_selection.details.cache.TrackSelectionDetailsCacheDataSourceImpl
import org.hyperskill.app.track_selection.details.data.repository.TrackSelectionDetailsRepositoryImpl
import org.hyperskill.app.track_selection.details.data.source.TrackSelectionDetailsCacheDataSource
import org.hyperskill.app.track_selection.details.domain.repository.TrackSelectionDetailsRepository

internal class TrackSelectionDetailsDataComponentImpl(
appGraph: AppGraph
) : TrackSelectionDetailsDataComponent {
private val trackSelectionDetailsCacheDataSource: TrackSelectionDetailsCacheDataSource =
TrackSelectionDetailsCacheDataSourceImpl(
json = appGraph.commonComponent.json,
settings = appGraph.commonComponent.settings
)

override val trackSelectionDetailsRepository: TrackSelectionDetailsRepository
get() = TrackSelectionDetailsRepositoryImpl(trackSelectionDetailsCacheDataSource)
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import org.hyperskill.app.profile.domain.repository.ProfileRepository
import org.hyperskill.app.providers.domain.repository.ProvidersRepository
import org.hyperskill.app.sentry.domain.interactor.SentryInteractor
import org.hyperskill.app.subscriptions.domain.repository.CurrentSubscriptionStateRepository
import org.hyperskill.app.track_selection.details.domain.repository.TrackSelectionDetailsRepository
import org.hyperskill.app.track_selection.details.presentation.TrackSelectionDetailsActionDispatcher
import org.hyperskill.app.track_selection.details.presentation.TrackSelectionDetailsFeature
import org.hyperskill.app.track_selection.details.presentation.TrackSelectionDetailsFeature.Action
Expand All @@ -26,14 +27,15 @@ import ru.nobird.app.presentation.redux.dispatcher.wrapWithActionDispatcher
import ru.nobird.app.presentation.redux.feature.Feature
import ru.nobird.app.presentation.redux.feature.ReduxFeature

object TrackSelectionDetailsFeatureBuilder {
internal object TrackSelectionDetailsFeatureBuilder {
private const val LOG_TAG = "TrackSelectionDetailsFeature"

fun build(
params: TrackSelectionDetailsParams,
resourceProvider: ResourceProvider,
dateFormatter: SharedDateFormatter,
currentSubscriptionStateRepository: CurrentSubscriptionStateRepository,
trackSelectionDetailsRepository: TrackSelectionDetailsRepository,
providersRepository: ProvidersRepository,
sentryInteractor: SentryInteractor,
profileRepository: ProfileRepository,
Expand All @@ -45,6 +47,7 @@ object TrackSelectionDetailsFeatureBuilder {
val reducer = TrackSelectionDetailsReducer().wrapWithLogger(buildVariant, logger, LOG_TAG)
val actionDispatcher = TrackSelectionDetailsActionDispatcher(
config = ActionDispatcherOptions(),
trackSelectionDetailsRepository = trackSelectionDetailsRepository,
providersRepository = providersRepository,
currentSubscriptionStateRepository = currentSubscriptionStateRepository,
sentryInteractor = sentryInteractor,
Expand Down
Loading

0 comments on commit 9940fa7

Please sign in to comment.