Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
71afc18
ANDR-71: Добавление нужных текстов для entry-point'а
PanMobile Feb 3, 2026
e692337
ANDR-71: Добавление сущности тренажера в бизнес логике главного экрана
PanMobile Feb 3, 2026
a713b92
ANDR-71: Добавление обработки команды по тренажеру
PanMobile Feb 3, 2026
b42e8a9
ANDR-71: Добавлена функция обработки навигации к тренажеру и передает…
PanMobile Feb 3, 2026
564d059
ANDR-71: Тест Превью с кнопкой тренажера
PanMobile Feb 3, 2026
16950a5
ANDR-71: Использование лямбды навигации для отправки комманд
PanMobile Feb 3, 2026
13011a5
ANDR-71: Изменена внешняя функция навигации-входа в фичу тренажера
PanMobile Feb 3, 2026
36b6221
ANDR-71: Добавлена реализация InterviewTrainerFeatureImpl. Внутри рег…
PanMobile Feb 3, 2026
ac244ec
ANDR-71: правки по ktlint'у
PanMobile Feb 3, 2026
0d696d1
ANDR-71: ScreenMapper теперь класс
PanMobile Feb 5, 2026
8d35990
ANDR-71: Константы названий экранов интервью-тренажера
PanMobile Feb 5, 2026
a62b5f7
ANDR-71: Реворк навигации
PanMobile Feb 5, 2026
8cfd053
ANDR-71: Улучшение экрана. Подключение вьюмодели и команд
PanMobile Feb 5, 2026
50271e4
ANDR-71: Вторичные Модули DI
PanMobile Feb 5, 2026
d766db4
ANDR-71: Вторичный маппер DI модуль
PanMobile Feb 5, 2026
384dadf
ANDR-71: Основной DI модуль экрана
PanMobile Feb 5, 2026
d9cc977
ANDR-71: Подключение модуля тренажера во все приложение
PanMobile Feb 5, 2026
063c8d0
ANDR-71: Подключение KOIN модуля
PanMobile Feb 5, 2026
ff7582d
ANDR-71: Исправлен DI модуль для юзкейса
PanMobile Feb 5, 2026
4758c41
ANDR-71: Добавил экрану скролл
PanMobile Feb 5, 2026
4eabdad
ANDR-71: Навигация с главного экрана на первый экран тренажера (Creat…
PanMobile Feb 5, 2026
d7dd6f1
ANDR-71: Создание путей для экрана тренажера
PanMobile Feb 5, 2026
84cc004
ANDR-71: Убран хардкод путь экрана CreateQuiz
PanMobile Feb 5, 2026
91ed820
ANDR-71: Исправлен класс теста
PanMobile Feb 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ dependencies {
implementation(project(":feature:questions-or-collections:impl"))
implementation(project(":feature:public-collections:impl"))
implementation(project(":feature:selection-specializations:impl"))
implementation(project(":feature:interview-trainer:api"))
implementation(project(":feature:interview-trainer:impl"))
}

tasks.withType<Test> {
Expand Down
4 changes: 3 additions & 1 deletion app/src/main/java/ru/yeahub/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ru.yeahub.detail_question.impl.di.detailQuestionFeatureModule
import ru.yeahub.example_details.impl.detailsFeatureModule
import ru.yeahub.example_home.impl.data.di.questionsMainFeatureModule
import ru.yeahub.example_profile.impl.profileFeatureModule
import ru.yeahub.interview_trainer.impl.createQuiz.di.createQuizModule
import ru.yeahub.navigation_impl.navigationPathModule
import ru.yeahub.network_impl.networkModule
import ru.yeahub.public_collections.impl.di.CollectionsFeatureModule
Expand Down Expand Up @@ -53,7 +54,8 @@ class Application : Application() {
CollectionsFeatureModule,
detailQuestionFeatureModule,
collectionsAndQuestionsFeatureModule,
specializationFeatureModule
specializationFeatureModule,
createQuizModule
)
}
// проверка, что модули загружены
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,9 @@ object FeatureRoute {

object InterviewTrainerFeature {
const val FEATURE_NAME = "interview_trainer"

const val CREATE_QUIZ_SCREEN_NAME = "create_quiz"
const val INTERVIEW_QUIZ_SCREEN_NAME = "interview_quiz"
const val INTERVIEW_QUIZ_RESULT_SCREEN_NAME = "interview_quiz_result"
}
}
3 changes: 3 additions & 0 deletions core/ui/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@
<string name="error_screen_title_text">УПС!</string>
<string name="on_back_button_text">Назад</string>
<string name="unknown_error_screen_text">Не удалось загрузить данные</string>

<string name="interview_trainer_title">Интервью тренажер</string>
<string name="interview_trainer_description">Улучшите свои знания перед собеседованием</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class QuestionMainFeatureImpl : FeatureApi {
navGraphBuilder: NavGraphBuilder,
navController: NavHostController,
pathManager: NavigationPathManager,
modifier: Modifier
modifier: Modifier,
) {
val currentPath = pathManager.getCurrentPath()
Timber.d("HomeFeatureImpl registerGraph: currentPath: $currentPath")
Expand All @@ -50,6 +50,9 @@ class QuestionMainFeatureImpl : FeatureApi {
},
onNavigateToCollections = {
handleCollectionsNavigation(pathManager, navController)
},
onNavigateToInterviewTrainer = {
handleInterviewTrainerNavigation(pathManager, navController)
}
)
}
Expand All @@ -60,7 +63,7 @@ class QuestionMainFeatureImpl : FeatureApi {
*/
private fun handleQuestionsNavigation(
pathManager: NavigationPathManager,
navController: NavHostController
navController: NavHostController,
) {
// Сбрасываем текущий путь на корневую фичу
pathManager.setCurrentPath(FeatureRoute.QuestionsFeature.FEATURE_NAME)
Expand All @@ -83,7 +86,7 @@ class QuestionMainFeatureImpl : FeatureApi {
*/
private fun handleCollectionsNavigation(
pathManager: NavigationPathManager,
navController: NavHostController
navController: NavHostController,
) {
// Сбрасываем текущий путь на корневую фичу
pathManager.setCurrentPath(FeatureRoute.CollectionsFeature.FEATURE_NAME)
Expand All @@ -100,4 +103,31 @@ class QuestionMainFeatureImpl : FeatureApi {
restoreState = true
}
}

/**
* Обработка навигации к интервью тренажеру.
*/
private fun handleInterviewTrainerNavigation(
pathManager: NavigationPathManager,
navController: NavHostController,
) {
val titleTopAppBar = "Test TitleTopAppBar"

// Сбрасываем текущий путь на корневую фичу
pathManager.setCurrentPath(FeatureRoute.InterviewTrainerFeature.FEATURE_NAME)

val createQuizPath = pathManager.createChildPath(
featureName = FeatureRoute.InterviewTrainerFeature.CREATE_QUIZ_SCREEN_NAME
) + "/" + titleTopAppBar

Timber.d("HomeFeatureImpl handleInterviewTrainerNavigation: Navigating to: $createQuizPath")

navController.navigate(createQuizPath) {
popUpTo(navController.graph.startDestinationId) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ package ru.yeahub.example_home.impl.presentation.intents
sealed class QuestionMainScreenCommand {
object NavigateToBaseQuestions : QuestionMainScreenCommand()
object NavigateToCollections : QuestionMainScreenCommand()
object NavigateToInterviewTrainer : QuestionMainScreenCommand()
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ class QuestionMainScreenMapper {
description = TextOrResource.Resource(R.string.base_questions_description),
imageRes = R.drawable.icon_base_question
),
//imageRes тренажера потом изменить на нормальный
QuestionMainUiModel(
type = QuestionMainItemType.InterviewTrainer,
title = TextOrResource.Resource(R.string.interview_trainer_title),
description = TextOrResource.Resource(R.string.interview_trainer_description),
imageRes = R.drawable.question_square
),
QuestionMainUiModel(
type = QuestionMainItemType.Collections,
title = TextOrResource.Resource(R.string.collections_title),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ package ru.yeahub.example_home.impl.presentation.model
sealed class QuestionMainItemType {
object BaseQuestions : QuestionMainItemType()
object Collections : QuestionMainItemType()
object InterviewTrainer : QuestionMainItemType()
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ import ru.yeahub.ui.R
fun QuestionsMainScreen(
onBackClick: () -> Unit,
onNavigateToBaseQuestions: () -> Unit,
onNavigateToCollections: () -> Unit
onNavigateToCollections: () -> Unit,
onNavigateToInterviewTrainer: () -> Unit,
) {
val viewModel: QuestionMainViewModel = koinViewModel()
val state by viewModel.state.collectAsStateWithLifecycle()
Expand All @@ -46,6 +47,7 @@ fun QuestionsMainScreen(
when (command) {
is QuestionMainScreenCommand.NavigateToBaseQuestions -> onNavigateToBaseQuestions()
is QuestionMainScreenCommand.NavigateToCollections -> onNavigateToCollections()
is QuestionMainScreenCommand.NavigateToInterviewTrainer -> onNavigateToInterviewTrainer()
}
}

Expand All @@ -60,7 +62,7 @@ fun QuestionsMainScreen(
fun QuestionsMainScreenContent(
state: QuestionMainScreenState,
onItemClick: (QuestionMainUiModel) -> Unit,
onBackClick: () -> Unit
onBackClick: () -> Unit,
) {
val context = LocalContext.current
val scrollState = rememberScrollState()
Expand Down Expand Up @@ -193,6 +195,13 @@ val stateWithContent = QuestionMainScreenState.Content(
description = TextOrResource.Resource(R.string.base_questions_description),
imageRes = R.drawable.icon_base_question
),
//imageRes тренажера потом изменить на нормальный
QuestionMainUiModel(
type = QuestionMainItemType.InterviewTrainer,
title = TextOrResource.Resource(R.string.interview_trainer_title),
description = TextOrResource.Resource(R.string.interview_trainer_description),
imageRes = R.drawable.question_square
),
QuestionMainUiModel(
type = QuestionMainItemType.Collections,
title = TextOrResource.Resource(R.string.collections_title),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ class QuestionMainViewModel(
)
QuestionMainItemType.Collections ->
_command.emit(QuestionMainScreenCommand.NavigateToCollections)

QuestionMainItemType.InterviewTrainer -> {
_command.emit(QuestionMainScreenCommand.NavigateToInterviewTrainer)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ import androidx.compose.runtime.Composable
*/
interface InterviewTrainerApi {
/**
* Экран тренажера собеседований.
* Главный/первый экран тренажера собеседований (CreateQuizScreen).
* Является entry-point'ом в фичу
* С него начинается вся внутренняя навигация
*
* @param onBackClick Действие при нажатии кнопки "Назад"
* @param onStartTrainingClick Действие при нажатии на старт тренировки в тренажере
*/
@Composable
fun InterviewTrainerScreen(
fun CreateQuizScreen(
onBackClick: () -> Unit,
onStartTrainingClick: (specializationId: String, questionsCount: String) -> Unit,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@ package ru.yeahub.interview_trainer.impl
import androidx.compose.ui.Modifier
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import ru.yeahub.interview_trainer.impl.createQuiz.presentation.CreateQuizResult
import ru.yeahub.interview_trainer.impl.createQuiz.ui.CreateQuizScreen
import ru.yeahub.navigation_api.FeatureApi
import ru.yeahub.navigation_api.FeatureRoute
import ru.yeahub.navigation_api.NavigationPathManager
import timber.log.Timber

class InterviewTrainerFeatureImpl : FeatureApi {
private const val TITLE_TOP_APP_BAR = "title"

class InterviewTrainerFeatureImpl() : FeatureApi {
override fun getFeatureName(): String = FeatureRoute.InterviewTrainerFeature.FEATURE_NAME

override fun registerGraph(
Expand All @@ -16,6 +24,88 @@ class InterviewTrainerFeatureImpl : FeatureApi {
pathManager: NavigationPathManager,
modifier: Modifier,
) {
TODO("Not yet implemented")
//Регистрируем базовый путь фичи (interview_trainer)
pathManager.registerFeaturePath(featureName = getFeatureName(), basePath = getFeatureName())

val featurePath = pathManager.getFeaturePath(getFeatureName()) ?: getFeatureName()

//Создаем путь экрана создания тренировки (interview_trainer/create_quiz/{title}
val createQuizRoute =
"$featurePath/${FeatureRoute.InterviewTrainerFeature.CREATE_QUIZ_SCREEN_NAME}/{$TITLE_TOP_APP_BAR}"

Timber.d("InterviewTrainerFeatureImpl registerGraph: currentPath: $createQuizRoute")

navGraphBuilder.composable(
route = createQuizRoute,
arguments = listOf(
navArgument(TITLE_TOP_APP_BAR) {
type = NavType.StringType
}
)
) { backStackEntry ->
val titleTopAppBar = backStackEntry.arguments?.getString(TITLE_TOP_APP_BAR) ?: ""

CreateQuizScreen(
onResult = { result ->
when (result) {
is CreateQuizResult.NavigateBack -> handleBackNavigation(
pathManager,
navController
)

is CreateQuizResult.NavigateToInterviewQuizScreen -> handleQuizNavigation(
pathManager = pathManager,
navController = navController,
titleTopAppBar = titleTopAppBar,
specializationId = result.specializationId.toString(),
questionsCount = result.questionCount.toString()
)
}
},
titleTopAppBar = titleTopAppBar
)
}
}

/**
* Обработка навигации назад.
*/
private fun handleBackNavigation(
pathManager: NavigationPathManager,
navController: NavHostController,
) {
val parentPath = pathManager.getParentPath()
Timber.d("InterviewTrainerFeatureImpl handleBackNavigation: Navigating to parent: $parentPath")

pathManager.setCurrentPath(parentPath)

if (parentPath.isEmpty()) {
navController.navigateUp()
} else {
navController.navigate(parentPath) {
popUpTo(parentPath) {
inclusive = true
}
}
}
}

/**
* Обработка навигации к экрану тренировки (InterviewQuizScreen).
*/
private fun handleQuizNavigation(
pathManager: NavigationPathManager,
navController: NavHostController,
titleTopAppBar: String,
specializationId: String,
questionsCount: String,
) {
val interviewQuizRoute = getFeatureName() + "/" +
FeatureRoute.InterviewTrainerFeature.INTERVIEW_QUIZ_SCREEN_NAME + "/" +
"$titleTopAppBar/$specializationId/$questionsCount"

Timber.d("InterviewTrainerFeatureImpl registerGraph: $interviewQuizRoute")

navController.navigate(interviewQuizRoute)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ru.yeahub.interview_trainer.impl.createQuiz.di

import org.koin.dsl.module
import ru.yeahub.interview_trainer.impl.createQuiz.data.CreateQuizDataToDomainMapper
import ru.yeahub.interview_trainer.impl.createQuiz.presentation.CreateQuizScreenMapper

val createQuizScreenMapperModule = module {
single { CreateQuizScreenMapper() }
}

val createQuizDataToDomainMapperModule = module {
factory { CreateQuizDataToDomainMapper() }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ru.yeahub.interview_trainer.impl.createQuiz.di

import org.koin.dsl.bind
import org.koin.dsl.module
import ru.yeahub.interview_trainer.impl.InterviewTrainerFeatureImpl
import ru.yeahub.navigation_api.FeatureApi

val createQuizModule = module {
includes(
createQuizScreenMapperModule,
createQuizDataToDomainMapperModule,
createQuizRepositoryModule,
createQuizUseCaseModule,
createQuizViewModelModule
)
single { InterviewTrainerFeatureImpl() } bind FeatureApi::class
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ru.yeahub.interview_trainer.impl.createQuiz.di

import org.koin.dsl.module
import ru.yeahub.interview_trainer.impl.createQuiz.data.CreateQuizRepositoryImpl
import ru.yeahub.interview_trainer.impl.createQuiz.domain.CreateQuizRepositoryApi

val createQuizRepositoryModule = module {
single<CreateQuizRepositoryApi> {
CreateQuizRepositoryImpl(
apiService = get(),
mapper = get()
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ru.yeahub.interview_trainer.impl.createQuiz.di

import org.koin.dsl.module
import ru.yeahub.interview_trainer.impl.createQuiz.domain.GetSpecializationsListUseCaseImpl
import ru.yeahub.interview_trainer.impl.createQuiz.domain.GetSpecializationsUseCase

val createQuizUseCaseModule = module {
factory<GetSpecializationsUseCase> {
GetSpecializationsListUseCaseImpl(
repository = get()
)
}
}
Loading