Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.team.prezel.core.model.presentation.PresentationScriptDetail
import com.team.prezel.core.model.presentation.PresentationWordDetail
import com.team.prezel.core.model.presentation.Purpose
import com.team.prezel.core.model.presentation.ScriptCorrection
import com.team.prezel.core.model.presentation.ScriptErrorType
import com.team.prezel.core.model.presentation.Style
import com.team.prezel.core.model.presentation.WordAnalysisDetail
import com.team.prezel.core.network.model.presentation.GetMainDataResponse
Expand Down Expand Up @@ -67,15 +68,13 @@ private fun PresentationExpectedQuestionResponse.toDomain(): ExpectedQuestion =

internal fun PresentationScriptDetailResponse.toDomain(): PresentationScriptDetail =
PresentationScriptDetail(
presentationId = presentationId,
audioUrl = audioUrl,
originalScript = originalScript,
scriptCorrections = scriptDetails.map { item -> item.toDomain() },
)

internal fun PresentationScriptAnalysisResponse.toDomain(): ScriptCorrection =
ScriptCorrection(
errorType = errorType,
errorType = ScriptErrorType.from(value = errorType),
sentence = sentence,
originalText = originalText,
correctedText = correctedText,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package com.team.prezel.core.domain.usecase.presentation

import com.team.prezel.core.domain.repository.presentation.PresentationRepository
import com.team.prezel.core.model.presentation.PresentationDetailWithPracticeRecords
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import javax.inject.Inject

class FetchPresentationDetailUseCase @Inject constructor(
Expand All @@ -13,23 +11,27 @@ class FetchPresentationDetailUseCase @Inject constructor(
presentationId: Long,
isPast: Boolean = false,
): Result<PresentationDetailWithPracticeRecords> =
runCatching {
coroutineScope {
val presentationDetailDeferred = async {
if (isPast) {
presentationRepository.getPastPresentationDetail(presentationId = presentationId).getOrThrow()
} else {
presentationRepository.getUpcomingPresentationDetail(presentationId = presentationId).getOrThrow()
}
}
val practiceRecordsDeferred = async {
presentationRepository.getPracticeRecords(presentationId = presentationId).getOrThrow()
}
if (isPast) {
getPastPresentationDetail(presentationId = presentationId)
} else {
getUpcomingPresentationDetail(presentationId = presentationId)
}

private suspend fun getPastPresentationDetail(presentationId: Long): Result<PresentationDetailWithPracticeRecords> =
presentationRepository
.getPastPresentationDetail(presentationId = presentationId)
.mapCatching { detail ->
val practiceRecords = presentationRepository.getPracticeRecords(presentationId = presentationId).getOrThrow()
PresentationDetailWithPracticeRecords(
analysisSummary = presentationDetailDeferred.await(),
practiceRecords = practiceRecordsDeferred.await(),
analysisSummary = detail,
practiceRecords = practiceRecords,
)
}
Comment thread
moondev03 marked this conversation as resolved.
}

private suspend fun getUpcomingPresentationDetail(presentationId: Long): Result<PresentationDetailWithPracticeRecords> =
presentationRepository
.getUpcomingPresentationDetail(presentationId = presentationId)
.mapCatching { detail ->
PresentationDetailWithPracticeRecords(analysisSummary = detail, practiceRecords = null)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ package com.team.prezel.core.model.presentation

data class PresentationDetailWithPracticeRecords(
val analysisSummary: PresentationAnalysisSummary,
val practiceRecords: PracticeRecords,
val practiceRecords: PracticeRecords?,
)
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
package com.team.prezel.core.model.presentation

data class PresentationScriptDetail(
val presentationId: Long,
val audioUrl: String,
val originalScript: String,
val scriptCorrections: List<ScriptCorrection>,
)

data class ScriptCorrection(
val errorType: String,
val errorType: ScriptErrorType,
val sentence: String,
val originalText: String,
val correctedText: String,
val reason: String,
)

enum class ScriptErrorType(
val value: String,
) {
GRAMMAR("GRAMMAR"),
SPELLING("SPELLING"),
;

companion object {
fun from(value: String): ScriptErrorType =
entries.find { entry ->
entry.value == value
} ?: throw IllegalArgumentException("지원하지 않는 에러 타입입니다.")
Comment thread
moondev03 marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package com.team.prezel.core.ui.component

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlin.math.abs

private const val DEFAULT_TOP_BAR_ANIMATION_DURATION_MILLIS = 200
private val DEFAULT_TOP_BAR_SCROLL_THRESHOLD = 48.dp

@Composable
fun HideOnScrollTopBarLayout(
scrollState: ScrollState,
topBar: @Composable () -> Unit,
body: @Composable () -> Unit,
modifier: Modifier = Modifier,
bottomBar: @Composable () -> Unit = {},
scrollThreshold: Dp = DEFAULT_TOP_BAR_SCROLL_THRESHOLD,
animationDurationMillis: Int = DEFAULT_TOP_BAR_ANIMATION_DURATION_MILLIS,
) {
val scrollThresholdPx = with(LocalDensity.current) { scrollThreshold.roundToPx() }
var isTopBarVisible by remember { mutableStateOf(true) }
var previousScrollPosition by remember { mutableIntStateOf(0) }
var topBarToggleAnchorPosition by remember { mutableIntStateOf(0) }

LaunchedEffect(scrollState, scrollThresholdPx) {
snapshotFlow { scrollState.value }
.map { currentScrollPosition ->
calculateTopBarVisibility(
currentScrollPosition = currentScrollPosition,
previousScrollPosition = previousScrollPosition,
topBarToggleAnchorPosition = topBarToggleAnchorPosition,
scrollThresholdPx = scrollThresholdPx,
isTopBarVisible = isTopBarVisible,
onScrollPositionChanged = { previousScrollPosition = it },
onAnchorChanged = { topBarToggleAnchorPosition = it },
)
}.distinctUntilChanged()
.collect { visible ->
isTopBarVisible = visible
}
}

Column(modifier = modifier.fillMaxSize()) {
AnimatedVisibility(
visible = isTopBarVisible,
enter = fadeIn(animationSpec = tween(animationDurationMillis)),
exit = fadeOut(animationSpec = tween(animationDurationMillis)),
) {
topBar()
}

Box(
modifier = Modifier
.weight(1f)
.verticalScroll(scrollState),
) {
body()
}

bottomBar()
}
}

private fun calculateTopBarVisibility(
currentScrollPosition: Int,
previousScrollPosition: Int,
topBarToggleAnchorPosition: Int,
scrollThresholdPx: Int,
isTopBarVisible: Boolean,
onScrollPositionChanged: (Int) -> Unit,
onAnchorChanged: (Int) -> Unit,
): Boolean {
val isAtTop = currentScrollPosition == 0
val isScrollingUp = currentScrollPosition < previousScrollPosition
val hasExceededThreshold = abs(currentScrollPosition - topBarToggleAnchorPosition) >= scrollThresholdPx

onScrollPositionChanged(currentScrollPosition)

return when {
isAtTop -> {
onAnchorChanged(0)
true
}
hasExceededThreshold && isScrollingUp -> {
onAnchorChanged(currentScrollPosition)
true
}
hasExceededThreshold && !isScrollingUp -> {
onAnchorChanged(currentScrollPosition)
false
}
else -> isTopBarVisible
}
}
1 change: 1 addition & 0 deletions Prezel/feature/report/impl/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
alias(libs.plugins.prezel.android.feature.impl)
alias(libs.plugins.kotlinx.serialization)
}

android {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package com.team.prezel.feature.report.impl.navigation

import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation3.runtime.EntryProviderScope
import androidx.navigation3.runtime.NavKey
import com.team.prezel.core.navigation.LocalNavigator
import com.team.prezel.feature.analysis.api.AnalysisNavKey
import com.team.prezel.feature.report.api.ReportNavKey
import com.team.prezel.feature.report.impl.AnalysisReportScreen
import com.team.prezel.feature.report.impl.AnalysisReportViewModel
import com.team.prezel.feature.report.impl.report.AnalysisReportScreen
import com.team.prezel.feature.report.impl.report.AnalysisReportViewModel
import com.team.prezel.feature.report.impl.script.ScriptScreen
import com.team.prezel.feature.report.impl.script.ScriptViewModel
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand Down Expand Up @@ -37,11 +40,24 @@ internal fun EntryProviderScope<NavKey>.featureAnalysisReportEntryBuilder() {
)
},
navigateToSelfFeedbackWrite = {},
navigateToScriptAnalysis = { analysisResultId ->
navigator.navigate(ReportInnerNavKey.ScriptCorrection(analysisResultId = analysisResultId))
},
viewModel = hiltViewModel<AnalysisReportViewModel, AnalysisReportViewModel.Factory>(
creationCallback = { factory -> factory.create(key) },
),
)
}
entry<ReportInnerNavKey.ScriptCorrection> { key ->
val navigator = LocalNavigator.current

ScriptScreen(
onClose = { navigator.goBack() },
viewModel = hiltViewModel<ScriptViewModel, ScriptViewModel.Factory>(
creationCallback = { factory -> factory.create(analysisResultId = key.analysisResultId) },
),
)
}
}

@Module
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.team.prezel.feature.report.impl.navigation

import androidx.navigation3.runtime.NavKey
import kotlinx.serialization.Serializable

@Serializable
sealed interface ReportInnerNavKey : NavKey {
@Serializable
data class ScriptCorrection(
val analysisResultId: Long,
) : ReportInnerNavKey
}
Loading