From c4245a99f892f5ef79c72cccae6a249e1afbeab6 Mon Sep 17 00:00:00 2001 From: Deyryl Date: Mon, 30 Mar 2026 15:35:59 +0300 Subject: [PATCH 1/5] =?UTF-8?q?ANDR-72:=20=D0=B8=D1=81=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20Parcelize=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B4=D0=B0=D1=87?= =?UTF-8?q?=D0=B8=20=D0=BC=D0=B5=D0=B6=D0=B4=D1=83=20=D1=8D=D0=BA=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/interview-trainer/impl/build.gradle.kts | 1 + .../interviewQuiz/presentation/VoQuestionWithAnswer.kt | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/feature/interview-trainer/impl/build.gradle.kts b/feature/interview-trainer/impl/build.gradle.kts index b6dbb5eb..494c89ce 100644 --- a/feature/interview-trainer/impl/build.gradle.kts +++ b/feature/interview-trainer/impl/build.gradle.kts @@ -2,6 +2,7 @@ plugins { alias(libs.plugins.android.library) alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) + alias(libs.plugins.kotlin.parcelize) } android { diff --git a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/presentation/VoQuestionWithAnswer.kt b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/presentation/VoQuestionWithAnswer.kt index cb3e3002..9d9b9690 100644 --- a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/presentation/VoQuestionWithAnswer.kt +++ b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/presentation/VoQuestionWithAnswer.kt @@ -1,16 +1,20 @@ package ru.yeahub.interview_trainer.impl.interviewQuiz.presentation +import android.os.Parcelable import androidx.compose.runtime.Immutable +import kotlinx.parcelize.Parcelize @Immutable +@Parcelize data class VoQuestionWithAnswer( val id: Long, val title: String, val shortAnswer: String, val userAnswer: QuizAnswerResult -) +) : Parcelable -enum class QuizAnswerResult { +@Parcelize +enum class QuizAnswerResult : Parcelable { KNOWN, UNKNOWN } From e1d4c93a54a450ab8439504676d4363b091b822b Mon Sep 17 00:00:00 2001 From: Deyryl Date: Mon, 30 Mar 2026 16:16:37 +0300 Subject: [PATCH 2/5] =?UTF-8?q?ANDR-72:=20=D0=B4=D0=BE=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BD=D0=B0=D0=B2=D0=B8=D0=B3?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/InterviewTrainerFeatureImpl.kt | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/InterviewTrainerFeatureImpl.kt b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/InterviewTrainerFeatureImpl.kt index 50eb3940..af512c14 100644 --- a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/InterviewTrainerFeatureImpl.kt +++ b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/InterviewTrainerFeatureImpl.kt @@ -8,12 +8,18 @@ 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.interview_trainer.impl.interviewQuiz.presentation.InterviewQuizResult +import ru.yeahub.interview_trainer.impl.interviewQuiz.presentation.VoQuestionWithAnswer +import ru.yeahub.interview_trainer.impl.interviewQuiz.ui.InterviewQuizScreen import ru.yeahub.navigation_api.FeatureApi import ru.yeahub.navigation_api.FeatureRoute import ru.yeahub.navigation_api.NavigationPathManager import timber.log.Timber private const val TITLE_TOP_APP_BAR = "title" +private const val SPECIALIZATION_ID = "specializationId" +private const val QUESTIONS_COUNT = "questionsCount" +private const val QUIZ_ANSWERS_KEY = "quizAnswersKey" private const val NOT_FOUND_NUMBER = 404 class InterviewTrainerFeatureImpl : FeatureApi { @@ -34,6 +40,12 @@ class InterviewTrainerFeatureImpl : FeatureApi { val createQuizRoute = "$featurePath/${FeatureRoute.InterviewTrainerFeature.CREATE_QUIZ_SCREEN_NAME}/{$TITLE_TOP_APP_BAR}" + // Регистрируем путь экрана тренировки + // (interview_trainer/interview_quiz/{titleId}/{specializationId}/{questionsCount}) + val interviewQuizRoute = + "$featurePath/${FeatureRoute.InterviewTrainerFeature.INTERVIEW_QUIZ_SCREEN_NAME}" + + "/{$TITLE_TOP_APP_BAR}/{$SPECIALIZATION_ID}/{$QUESTIONS_COUNT}" + Timber.d("InterviewTrainerFeatureImpl registerGraph: currentPath: $createQuizRoute") navGraphBuilder.composable( @@ -67,6 +79,38 @@ class InterviewTrainerFeatureImpl : FeatureApi { } CreateQuizScreen(onResult = onResult, titleTopAppBarResId = titleTopAppBarResId) } + + navGraphBuilder.composable( + route = interviewQuizRoute, + arguments = listOf( + navArgument(TITLE_TOP_APP_BAR) { type = NavType.IntType }, + navArgument(SPECIALIZATION_ID) { type = NavType.StringType }, + navArgument(QUESTIONS_COUNT) { type = NavType.StringType }, + ) + ) { backStackEntry -> + val titleTopAppBarResId = + backStackEntry.arguments?.getInt(TITLE_TOP_APP_BAR) + ?: NOT_FOUND_NUMBER + + InterviewQuizScreen( + onResult = { result -> + when (result) { + is InterviewQuizResult.NavigateBack -> handleBackNavigation( + pathManager = pathManager, + navController = navController + ) + + is InterviewQuizResult.NavigateToInterviewQuizResultScreen -> handleQuizResultNavigation( + pathManager = pathManager, + navController = navController, + featurePath = featurePath, + titleTopAppBarResId = titleTopAppBarResId, + questionsWithAnswersList = result.questionsWithAnswersList + ) + } + } + ) + } } /** @@ -113,4 +157,28 @@ class InterviewTrainerFeatureImpl : FeatureApi { navController.navigate(interviewQuizRoute) pathManager.setCurrentPath(interviewQuizRoute) } + + /** + * Обработка навигации к экрану результата тренировки (InterviewQuizResultScreen) + */ + private fun handleQuizResultNavigation( + pathManager: NavigationPathManager, + navController: NavHostController, + featurePath: String, + titleTopAppBarResId: Int, + questionsWithAnswersList: List + ) { + navController.currentBackStackEntry + ?.savedStateHandle + ?.set(QUIZ_ANSWERS_KEY, ArrayList(questionsWithAnswersList)) + + val interviewQuizResultRoute = + "$featurePath/${FeatureRoute.InterviewTrainerFeature.INTERVIEW_QUIZ_RESULT_SCREEN_NAME}" + + "/$titleTopAppBarResId" + + Timber.d("InterviewTrainerFeatureImpl registerGraph: $interviewQuizResultRoute") + + navController.navigate(interviewQuizResultRoute) + pathManager.setCurrentPath(interviewQuizResultRoute) + } } \ No newline at end of file From dc6039d91b4cc34e6d09e8771872f23ff3084412 Mon Sep 17 00:00:00 2001 From: Deyryl Date: Mon, 1 Jun 2026 18:17:49 +0300 Subject: [PATCH 3/5] =?UTF-8?q?ANDR-72:=20=D0=BD=D0=B0=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=B9=D0=BA=D0=B0=20DI,=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BC=D0=B8=D0=BD=D0=BE?= =?UTF-8?q?=D1=80=D0=BD=D1=8B=D1=85=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BE=D0=BA?= =?UTF-8?q?,=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20QuizAnswersWrapperDto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/ru/yeahub/Application.kt | 2 ++ .../models/GetNewMockQuizResponse.kt | 2 +- .../network_api/models/QuestionAnswerDto.kt | 2 +- .../models/QuizAnswersWrapperDto.kt | 5 +++ .../yeahub/network_impl/RetrofitApiService.kt | 2 +- .../interviewQuiz/di/InterviewQuizModule.kt | 36 +++++++++++++++++++ .../presentation/InterviewQuizViewModel.kt | 4 ++- .../interviewQuiz/ui/InterviewQuizScreen.kt | 2 +- .../InterviewQuizDataToDomainMapperTest.kt | 3 +- 9 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 core/network-api/src/main/java/ru/yeahub/network_api/models/QuizAnswersWrapperDto.kt create mode 100644 feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/di/InterviewQuizModule.kt diff --git a/app/src/main/java/ru/yeahub/Application.kt b/app/src/main/java/ru/yeahub/Application.kt index 8d2a0b8d..99059246 100644 --- a/app/src/main/java/ru/yeahub/Application.kt +++ b/app/src/main/java/ru/yeahub/Application.kt @@ -8,6 +8,7 @@ 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.interview_trainer.impl.interviewQuiz.di.interviewQuizModule import ru.yeahub.interview_trainer.impl.interviewQuizResult.di.interviewQuizResultModule import ru.yeahub.navigation_impl.navigationPathModule import ru.yeahub.network_impl.networkModule @@ -57,6 +58,7 @@ class Application : Application() { collectionsAndQuestionsFeatureModule, specializationFeatureModule, createQuizModule, + interviewQuizModule, interviewQuizResultModule ) } diff --git a/core/network-api/src/main/java/ru/yeahub/network_api/models/GetNewMockQuizResponse.kt b/core/network-api/src/main/java/ru/yeahub/network_api/models/GetNewMockQuizResponse.kt index 6584f201..2d283972 100644 --- a/core/network-api/src/main/java/ru/yeahub/network_api/models/GetNewMockQuizResponse.kt +++ b/core/network-api/src/main/java/ru/yeahub/network_api/models/GetNewMockQuizResponse.kt @@ -5,6 +5,6 @@ data class GetNewMockQuizResponse( val startDate: String, val fullCount: Int, val skills: List, - val response: List, + val response: QuizAnswersWrapperDto, val questions: List ) \ No newline at end of file diff --git a/core/network-api/src/main/java/ru/yeahub/network_api/models/QuestionAnswerDto.kt b/core/network-api/src/main/java/ru/yeahub/network_api/models/QuestionAnswerDto.kt index ac8ed579..89c1f01b 100644 --- a/core/network-api/src/main/java/ru/yeahub/network_api/models/QuestionAnswerDto.kt +++ b/core/network-api/src/main/java/ru/yeahub/network_api/models/QuestionAnswerDto.kt @@ -1,7 +1,7 @@ package ru.yeahub.network_api.models data class QuestionAnswerDto( - val questionId: Int, + val questionId: Long, val questionTitle: String, val answer: String ) diff --git a/core/network-api/src/main/java/ru/yeahub/network_api/models/QuizAnswersWrapperDto.kt b/core/network-api/src/main/java/ru/yeahub/network_api/models/QuizAnswersWrapperDto.kt new file mode 100644 index 00000000..c234fadc --- /dev/null +++ b/core/network-api/src/main/java/ru/yeahub/network_api/models/QuizAnswersWrapperDto.kt @@ -0,0 +1,5 @@ +package ru.yeahub.network_api.models + +data class QuizAnswersWrapperDto( + val answers: List +) diff --git a/core/network-impl/src/main/java/ru/yeahub/network_impl/RetrofitApiService.kt b/core/network-impl/src/main/java/ru/yeahub/network_impl/RetrofitApiService.kt index dbfcc56c..28542684 100644 --- a/core/network-impl/src/main/java/ru/yeahub/network_impl/RetrofitApiService.kt +++ b/core/network-impl/src/main/java/ru/yeahub/network_impl/RetrofitApiService.kt @@ -63,7 +63,7 @@ interface RetrofitApiService : ApiService { @Query("isFree") isFree: Boolean ): GetCollectionsResponse - @GET("/interview-preparation/quizzes/mock/new") + @GET("interview-preparation/quizzes/mock/new") override suspend fun getQuizMockQuestions( @Query("skills") skills: List?, @Query("complexity") complexity: List?, diff --git a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/di/InterviewQuizModule.kt b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/di/InterviewQuizModule.kt new file mode 100644 index 00000000..021795d4 --- /dev/null +++ b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/di/InterviewQuizModule.kt @@ -0,0 +1,36 @@ +package ru.yeahub.interview_trainer.impl.interviewQuiz.di + +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.dsl.module +import ru.yeahub.interview_trainer.impl.interviewQuiz.data.InterviewQuizDataToDomainMapper +import ru.yeahub.interview_trainer.impl.interviewQuiz.data.InterviewQuizRepositoryImpl +import ru.yeahub.interview_trainer.impl.interviewQuiz.domain.GetQuestionsListUseCase +import ru.yeahub.interview_trainer.impl.interviewQuiz.domain.GetQuestionsListUseCaseImpl +import ru.yeahub.interview_trainer.impl.interviewQuiz.domain.InterviewQuizRepositoryApi +import ru.yeahub.interview_trainer.impl.interviewQuiz.presentation.InterviewQuizScreenMapper +import ru.yeahub.interview_trainer.impl.interviewQuiz.presentation.InterviewQuizViewModel + +val interviewQuizModule = module { + // Мапперы + single { InterviewQuizDataToDomainMapper() } + single { InterviewQuizScreenMapper() } + + // Репозиторий + single { + InterviewQuizRepositoryImpl(networkProvider = get(), mapper = get()) + } + + // Юзкейс + single { + GetQuestionsListUseCaseImpl(repository = get()) + } + + // Вьюмодель экрана + viewModel { params -> + InterviewQuizViewModel( + savedStateHandle = params.get(), + screenMapper = get(), + getQuestionsListUseCase = get() + ) + } +} \ No newline at end of file diff --git a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/presentation/InterviewQuizViewModel.kt b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/presentation/InterviewQuizViewModel.kt index af88a583..d07a8fc7 100644 --- a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/presentation/InterviewQuizViewModel.kt +++ b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/presentation/InterviewQuizViewModel.kt @@ -18,6 +18,7 @@ import ru.yeahub.core_utils.BaseViewModel import ru.yeahub.interview_trainer.impl.interviewQuiz.domain.GetQuestionsListUseCase import ru.yeahub.interview_trainer.impl.interviewQuiz.domain.QuestionsRequest import ru.yeahub.interview_trainer.impl.interviewQuiz.presentation.InterviewQuizState.Loaded.QuizAnswer +import timber.log.Timber open class InterviewQuizViewModel( savedStateHandle: SavedStateHandle, @@ -56,7 +57,8 @@ open class InterviewQuizViewModel( selectedAnswer = userInput.selectedAnswer ) }.catch { e -> - screenMapper.getScreenState(e) + Timber.d("$e") + emit(screenMapper.getScreenState(e)) }.stateIn( scope = viewModelScopeSafe, started = SharingStarted.WhileSubscribed(TIME_TO_CLEAN_UP_RESOURCES), diff --git a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/ui/InterviewQuizScreen.kt b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/ui/InterviewQuizScreen.kt index bde28292..dd1eac61 100644 --- a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/ui/InterviewQuizScreen.kt +++ b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/ui/InterviewQuizScreen.kt @@ -366,7 +366,7 @@ private fun QuestionCard( } } else { SecondaryButton( - onClick = {}, + onClick = onResultClick, modifier = Modifier .width(170.dp) .height(48.dp) diff --git a/feature/interview-trainer/impl/src/test/java/test/InterviewQuizDataToDomainMapperTest.kt b/feature/interview-trainer/impl/src/test/java/test/InterviewQuizDataToDomainMapperTest.kt index af02ea37..e75012bf 100644 --- a/feature/interview-trainer/impl/src/test/java/test/InterviewQuizDataToDomainMapperTest.kt +++ b/feature/interview-trainer/impl/src/test/java/test/InterviewQuizDataToDomainMapperTest.kt @@ -9,6 +9,7 @@ import ru.yeahub.interview_trainer.impl.interviewQuiz.domain.DomainQuestionsList import ru.yeahub.network_api.models.GetNewMockQuizResponse import ru.yeahub.network_api.models.GetQuestionResponse import ru.yeahub.network_api.models.NestedUserReferenceDto +import ru.yeahub.network_api.models.QuizAnswersWrapperDto import ru.yeahub.test.TestArgumentsProvider class InterviewQuizDataToDomainMapperTest { @@ -87,7 +88,7 @@ class InterviewQuizDataToDomainMapperTest { startDate = "01.01.2026", fullCount = 2, skills = emptyList(), - response = emptyList(), + response = QuizAnswersWrapperDto(emptyList()), questions = listOf(defaultQuestionResponse, defaultQuestionResponseWithLongAnswer), ) From a6286f944b7daf8f15d8cf0a360ac76e4f67e4a2 Mon Sep 17 00:00:00 2001 From: Deyryl Date: Mon, 1 Jun 2026 18:40:35 +0300 Subject: [PATCH 4/5] =?UTF-8?q?ANDR-72:=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BB=D0=B8=D1=88=D0=BD=D0=B5=D0=B9=20?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=BE=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/interviewQuiz/presentation/InterviewQuizViewModel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/presentation/InterviewQuizViewModel.kt b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/presentation/InterviewQuizViewModel.kt index d07a8fc7..b752d951 100644 --- a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/presentation/InterviewQuizViewModel.kt +++ b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/presentation/InterviewQuizViewModel.kt @@ -57,7 +57,6 @@ open class InterviewQuizViewModel( selectedAnswer = userInput.selectedAnswer ) }.catch { e -> - Timber.d("$e") emit(screenMapper.getScreenState(e)) }.stateIn( scope = viewModelScopeSafe, From ce16e04dc514569b42c69cc35ce7a966eebf64a1 Mon Sep 17 00:00:00 2001 From: Deyryl Date: Mon, 1 Jun 2026 18:40:53 +0300 Subject: [PATCH 5/5] =?UTF-8?q?ANDR-72:=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BB=D0=B8=D1=88=D0=BD=D0=B5=D0=B3=D0=BE?= =?UTF-8?q?=20=D0=B8=D0=BC=D0=BF=D0=BE=D1=80=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/interviewQuiz/presentation/InterviewQuizViewModel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/presentation/InterviewQuizViewModel.kt b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/presentation/InterviewQuizViewModel.kt index b752d951..9001e668 100644 --- a/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/presentation/InterviewQuizViewModel.kt +++ b/feature/interview-trainer/impl/src/main/java/ru/yeahub/interview_trainer/impl/interviewQuiz/presentation/InterviewQuizViewModel.kt @@ -18,7 +18,6 @@ import ru.yeahub.core_utils.BaseViewModel import ru.yeahub.interview_trainer.impl.interviewQuiz.domain.GetQuestionsListUseCase import ru.yeahub.interview_trainer.impl.interviewQuiz.domain.QuestionsRequest import ru.yeahub.interview_trainer.impl.interviewQuiz.presentation.InterviewQuizState.Loaded.QuizAnswer -import timber.log.Timber open class InterviewQuizViewModel( savedStateHandle: SavedStateHandle,