Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 8 additions & 1 deletion .idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -104,38 +104,39 @@ fun PopoverQuestion(
verticalArrangement = Arrangement.spacedBy(rowSpacing),
) {
// Первый ряд: Изучить, Повторить, Избранное
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
IconWithText(
iconResId = R.drawable.student,
text = stringResource(R.string.learn),
contentDescription = stringResource(R.string.learn),
isClickable = onLearnClick != null,
onItemClickListener = { onLearnClick?.invoke() },
screenWidth = screenWidth
)
IconWithText(
iconResId = R.drawable.clockcounterclockwise,
text = stringResource(R.string.repeat),
contentDescription = stringResource(R.string.repeat),
isClickable = onRepeatClick != null,
onItemClickListener = { onRepeatClick?.invoke() },
screenWidth = screenWidth
)
IconWithText(
iconResId = getFavoriteIconRes(favoriteState),
text = stringResource(R.string.favorite),
contentDescription = stringResource(R.string.favorite),
isClickable = favoriteState.isClickable,
iconColor = getFavoriteIconColor(favoriteState),
textColor = getFavoriteTextColor(favoriteState),
onItemClickListener = { onFavoriteClick?.invoke() },
screenWidth = screenWidth
)
if (false) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly,
) {
IconWithText(
iconResId = R.drawable.student,
text = stringResource(R.string.learn),
contentDescription = stringResource(R.string.learn),
isClickable = onLearnClick != null,
onItemClickListener = { onLearnClick?.invoke() },
screenWidth = screenWidth
)
IconWithText(
iconResId = R.drawable.clockcounterclockwise,
text = stringResource(R.string.repeat),
contentDescription = stringResource(R.string.repeat),
isClickable = onRepeatClick != null,
onItemClickListener = { onRepeatClick?.invoke() },
screenWidth = screenWidth
)
IconWithText(
iconResId = getFavoriteIconRes(favoriteState),
text = stringResource(R.string.favorite),
contentDescription = stringResource(R.string.favorite),
isClickable = favoriteState.isClickable,
iconColor = getFavoriteIconColor(favoriteState),
textColor = getFavoriteTextColor(favoriteState),
onItemClickListener = { onFavoriteClick?.invoke() },
screenWidth = screenWidth
)
}
}

// Второй ряд: Назад, Вперед
Row(
modifier = Modifier.fillMaxWidth(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import ru.yeahub.detail_question.impl.presentation.view.DetailQuestionScreen
import ru.yeahub.navigation_api.FeatureApi
import ru.yeahub.navigation_api.FeatureRoute
import ru.yeahub.navigation_api.NavigationPathManager
import timber.log.Timber

class DetailQuestionFeatureImpl : FeatureApi {

Expand All @@ -28,50 +29,111 @@ class DetailQuestionFeatureImpl : FeatureApi {
) {
val detailQuestionRoute = pathManager.createParametrizedPath(
featureName = getFeatureName(),
"questionId"
"questionIds",
"currentIndex"
)

navGraphBuilder.composable(
route = detailQuestionRoute,
arguments = listOf(
navArgument("questionId") { type = NavType.LongType }
navArgument("questionIds") { type = NavType.StringType },
navArgument("currentIndex") { type = NavType.IntType }
)
) { backStackEntry ->
val questionId = backStackEntry.arguments?.getLong("questionId") ?: 0L
val questionIdsString = backStackEntry.arguments?.getString("questionIds") ?: ""
val questionIds = questionIdsString.split(",").map { it.toLongOrNull() ?: 0L }
val currentIndex = backStackEntry.arguments?.getInt("currentIndex") ?: 0
val currentQuestionId = questionIds.getOrNull(currentIndex) ?: 0L

// Извлекаем родительский путь из текущего маршрута, а не из pathManager
val currentRoute = backStackEntry.destination.route ?: ""
val parentPathFromRoute = extractParentPath(currentRoute)

Timber.tag("DetailQuestionFeature")
.d("Current route: $currentRoute, Parent path: $parentPathFromRoute")

DetailQuestionScreen(
onResult = { result ->
when (result) {
DetailQuestionResult.BackClick -> handleBackNavigation(
pathManager,
navController
)
is DetailQuestionResult.BackClick -> handleBackNavigation(navController)

is DetailQuestionResult.UrlClick -> {
val intent = Intent(Intent.ACTION_VIEW, result.url.toUri())
navController.context.startActivity(intent)
}

is DetailQuestionResult.NavigateToQuestion -> handleQuestionNavigation(
navController,
questionIds,
result.newIndex,
parentPathFromRoute
)
}
},
questionId = questionId
questionId = currentQuestionId,
questionIds = questionIds,
currentIndex = currentIndex
)
}
}
}

private fun handleBackNavigation(
pathManager: NavigationPathManager,
navController: NavHostController
) {
val parentPath = pathManager.getParentPath()

pathManager.setCurrentPath(parentPath)
// Используем navigateUp для возврата на предыдущий экран (список вопросов)
Timber.tag("DetailQuestion")
.d("handleBackNavigation: Using navigateUp to return to questions list")
navController.navigateUp()
}

if (parentPath.isEmpty()) {
navController.navigateUp()
private fun handleQuestionNavigation(
navController: NavHostController,
questionIds: List<Long>,
newIndex: Int,
parentPath: String
) {
val questionIdsParam = questionIds.joinToString(",")
val concretePath = if (parentPath.isEmpty()) {
"${FeatureRoute.DetailQuestionFeature.FEATURE_NAME}/$questionIdsParam/$newIndex"
} else {
navController.navigate(parentPath) {
popUpTo(parentPath) {
"$parentPath/${FeatureRoute.DetailQuestionFeature.FEATURE_NAME}/$questionIdsParam/$newIndex"
}

Timber.tag("DetailQuestionFeature")
.d("Navigating to: $concretePath (parent: $parentPath)")

// Получаем текущий маршрут из стека для popUpTo
val currentRoute = navController.currentBackStackEntry?.destination?.route

navController.navigate(concretePath) {
// Удаляем текущий экран детального вопроса из стека
if (currentRoute != null) {
popUpTo(currentRoute) {
inclusive = true
}
}
// Предотвращаем создание нескольких копий одного экрана
launchSingleTop = true
}
}

/**
* Извлекает родительский путь из текущего маршрута.
* Например, из "questions/detail_question/{questionIds}/{currentIndex}"
* извлечет "questions"
*/
private fun extractParentPath(route: String): String {
if (route.isEmpty()) return ""

// Удаляем параметры маршрута (все что после /)
val featureName = FeatureRoute.DetailQuestionFeature.FEATURE_NAME
val featureIndex = route.indexOf(featureName)

return if (featureIndex > 0) {
// Возвращаем все до имени фичи detail_question (без последнего /)
route.substring(0, featureIndex - 1)
} else {
""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ package ru.yeahub.detail_question.impl.presentation.intents
internal sealed class DetailQuestionCommand {
data object NavigateBack : DetailQuestionCommand()
data class OpenUrl(val url: String) : DetailQuestionCommand()
data class NavigateToQuestion(val newIndex: Int) : DetailQuestionCommand()
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ internal sealed class DetailQuestionEvent {
data object OnBackClick : DetailQuestionEvent()
data class OnTelegramClick(val url: String) : DetailQuestionEvent()
data class OnYoutubeClick(val url: String) : DetailQuestionEvent()
data class OnPreviousQuestionClick(val currentQuestionId: Long) : DetailQuestionEvent()
data class OnNextQuestionClick(val currentQuestionId: Long) : DetailQuestionEvent()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ package ru.yeahub.detail_question.impl.presentation.intents
sealed class DetailQuestionResult {
data object BackClick : DetailQuestionResult()
data class UrlClick(val url: String) : DetailQuestionResult()
data class NavigateToQuestion(val newIndex: Int) : DetailQuestionResult()
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,24 @@ const val ELLIPSIS_LENGTH = 3
@Composable
fun DetailQuestionScreen(
onResult: (DetailQuestionResult) -> Unit,
questionId: Long
questionId: Long,
questionIds: List<Long>,
currentIndex: Int = 0
) {
val viewModel: DetailQuestionViewModel = koinViewModel()

LaunchedEffect(Unit) {
LaunchedEffect(questionId) {
viewModel.handleEvents(DetailQuestionEvent.LoadQuestion(questionId))
}

DetailQuestionScreenView(
onBackClick = { viewModel.handleEvents(DetailQuestionEvent.OnBackClick) },
viewModel = viewModel
viewModel = viewModel,
questionIds = questionIds,
currentIndex = currentIndex,
onNavigateToQuestion = { newIndex ->
onResult(DetailQuestionResult.NavigateToQuestion(newIndex))
}
)

HandleCommands(viewModel.commands, onResult)
Expand All @@ -71,8 +78,11 @@ internal fun HandleCommands(
LaunchedEffect(Unit) {
commands.collect { command ->
when (command) {
DetailQuestionCommand.NavigateBack -> onResult(DetailQuestionResult.BackClick)
is DetailQuestionCommand.NavigateBack -> onResult(DetailQuestionResult.BackClick)
is DetailQuestionCommand.OpenUrl -> onResult(DetailQuestionResult.UrlClick(command.url))
is DetailQuestionCommand.NavigateToQuestion -> onResult(
DetailQuestionResult.NavigateToQuestion(command.newIndex)
)
}
}
}
Expand All @@ -82,9 +92,14 @@ internal fun HandleCommands(
@Composable
fun DetailQuestionScreenView(
onBackClick: () -> Unit,
viewModel: DetailQuestionViewModel
viewModel: DetailQuestionViewModel,
questionIds: List<Long>,
currentIndex: Int = 0,
onNavigateToQuestion: (Int) -> Unit = {}
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val hasPrevious = currentIndex > 0
val hasNext = currentIndex < questionIds.size - 1
val configuration = LocalConfiguration.current
val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
Scaffold(
Expand Down Expand Up @@ -137,6 +152,16 @@ fun DetailQuestionScreenView(
)
}
},
onPreviousQuestionClick = {
if (hasPrevious) {
onNavigateToQuestion(currentIndex - 1)
}
},
onNextQuestionClick = {
if (hasNext) {
onNavigateToQuestion(currentIndex + 1)
}
},
padding = PaddingValues(
top = paddingValues.calculateTopPadding()
)
Expand All @@ -150,6 +175,8 @@ fun DetailQuestionScreenState(
onBackClick: () -> Unit,
onTelegramClick: () -> Unit,
onYoutubeClick: () -> Unit,
onPreviousQuestionClick: () -> Unit,
onNextQuestionClick: () -> Unit,
padding: PaddingValues
) {
when (uiState) {
Expand All @@ -158,6 +185,8 @@ fun DetailQuestionScreenState(
uiState.data,
onTelegramClick,
onYoutubeClick,
onPreviousQuestionClick,
onNextQuestionClick,
padding,
TextOrResource.Resource(R.string.guru_description_text)
)
Expand Down Expand Up @@ -286,7 +315,9 @@ fun StatesDetailQuestionPreview(params: DetailQuestionScreenStateParams) {
padding = params.padding,
onTelegramClick = {},
onYoutubeClick = {},
onBackClick = {}
onBackClick = {},
onPreviousQuestionClick = {},
onNextQuestionClick = {}
)
}

Expand Down Expand Up @@ -396,5 +427,6 @@ fun DetailQuestionScreenDynamicPreview() {
DetailQuestionScreenView(
onBackClick = {},
viewModel = mockViewModel,
questionIds = emptyList()
)
}
Loading