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
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,15 @@ internal class ConnectRepositoryImpl @Inject constructor(
return response.toDomain()
}

override suspend fun getHomeDialogHiddenDate(): Flow<LocalDateTime?> =
override suspend fun getExplicitDeniedConnectDate(): Flow<LocalDateTime?> =
connectLocalDataSource.explicitDeniedConnectDate.map { dateString ->
dateString?.let {
runCatching { dateFormatter.parseDateTime(it) }
.getOrDefault(LocalDateTime.MIN)
}
}

override suspend fun updateHomeDialogHiddenDate(date: LocalDateTime) {
override suspend fun updateExplicitDeniedConnectDate(date: LocalDateTime) {
val formattedDate = dateFormatter.format(date)
connectLocalDataSource.updateExplicitDeniedConnectDate(formattedDate)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ interface ConnectRepository {
traineeId: String,
): ConnectedResult

suspend fun getHomeDialogHiddenDate(): Flow<LocalDateTime?>
suspend fun getExplicitDeniedConnectDate(): Flow<LocalDateTime?>

suspend fun updateHomeDialogHiddenDate(date: LocalDateTime)
suspend fun updateExplicitDeniedConnectDate(date: LocalDateTime)
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ internal class TraineeHomeViewModel @Inject constructor(
private fun updateCurrentDateTime() {
val currentDateTime = LocalDateTime.now()
viewModelScope.launch {
connectRepository.updateHomeDialogHiddenDate(currentDateTime)
connectRepository.updateExplicitDeniedConnectDate(currentDateTime)
}
}

Expand All @@ -169,7 +169,7 @@ internal class TraineeHomeViewModel @Inject constructor(
sendEffect(TraineeHomeEffect.ShowToast("서버 요청에 실패했어요."))
}

val lastHiddenDate = connectRepository.getHomeDialogHiddenDate().firstOrNull()
val lastHiddenDate = connectRepository.getExplicitDeniedConnectDate().firstOrNull()
val isHidden = lastHiddenDate != null &&
Duration.between(lastHiddenDate, currentDateTime).toHours() < DIALOG_HIDE_DURATION_HOURS

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,15 @@ internal class TrainerHomeContract {
val selectedDay: LocalDate = LocalDate.now(),
val dailyPtSessionCount: Map<LocalDate, Int> = mapOf(),
val selectedDayPtSessions: List<PtSession>? = null,
) : UiState
val dialogState: DialogState = DialogState.NONE,
val isDialogHiddenForThreeDays: Boolean = false,
) : UiState {
enum class DialogState {
NONE,
HOME_CONNECT,
ADD_PT_CONNECT,
}
}

sealed interface TrainerHomeUiEvent : UiEvent {
data object OnScreen : TrainerHomeUiEvent
Expand All @@ -21,11 +29,15 @@ internal class TrainerHomeContract {
data class OnClickDay(val day: LocalDate) : TrainerHomeUiEvent
data object OnClickAddPtSession : TrainerHomeUiEvent
data class OnClickPtSessionComplete(val ptSession: PtSession) : TrainerHomeUiEvent
data object OnConfirmConnectDialog : TrainerHomeUiEvent
data object OnChangeHideDialogOption : TrainerHomeUiEvent
data object OnDismissDialog : TrainerHomeUiEvent
}

sealed interface TrainerHomeSideEffect : UiSideEffect {
data object NavigateToNotification : TrainerHomeSideEffect
data object NavigateToAddPtSession : TrainerHomeSideEffect
data object NavigateToInvite : TrainerHomeSideEffect
data class ShowToast(val message: String) : TrainerHomeSideEffect
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
Expand All @@ -38,6 +39,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.kr.tnt.core.designsystem.R
import co.kr.tnt.designsystem.component.TnTPopupDialog
import co.kr.tnt.designsystem.component.button.TnTFabButton
import co.kr.tnt.designsystem.component.calendar.TnTIndicatorMonthCalendar
import co.kr.tnt.designsystem.component.calendar.model.DayIndicatorState
Expand All @@ -51,6 +53,7 @@ import co.kr.tnt.domain.utils.DateFormatter
import co.kr.tnt.trainer.home.TrainerHomeContract.TrainerHomeSideEffect
import co.kr.tnt.trainer.home.TrainerHomeContract.TrainerHomeUiEvent
import co.kr.tnt.trainer.home.TrainerHomeContract.TrainerHomeUiState
import co.kr.tnt.ui.component.TnTCheckToggleDialog
import co.kr.tnt.ui.component.TnTHomeTopBar
import coil.compose.rememberAsyncImagePainter
import coil.request.ImageRequest
Expand All @@ -60,13 +63,15 @@ import kotlinx.coroutines.launch
import java.time.DayOfWeek
import java.time.LocalDate
import java.time.YearMonth
import co.kr.tnt.core.ui.R as coreR

@Composable
internal fun TrainerHomeRoute(
viewModel: TrainerHomeViewModel = hiltViewModel(),
padding: PaddingValues,
navigateToNotification: () -> Unit,
navigateToAddPtSession: () -> Unit,
navigateToInvite: (Boolean) -> Unit,
) {
val toast = LocalSnackbar.current
val state by viewModel.uiState.collectAsStateWithLifecycle()
Expand All @@ -86,11 +91,42 @@ internal fun TrainerHomeRoute(
when (effect) {
TrainerHomeSideEffect.NavigateToNotification -> navigateToNotification()
TrainerHomeSideEffect.NavigateToAddPtSession -> navigateToAddPtSession()
TrainerHomeSideEffect.NavigateToInvite -> navigateToInvite(false)
is TrainerHomeSideEffect.ShowToast -> toast.show(effect.message)
}
}
}

when (state.dialogState) {
TrainerHomeUiState.DialogState.NONE -> Unit
TrainerHomeUiState.DialogState.HOME_CONNECT -> {
TnTCheckToggleDialog(
title = "회원을 연결해 주세요",
content = "연결하지 않을 경우 수업을 추가할 수 없어요\n초대 코드를 복사해 연결해주시겠어요?",
isChecked = state.isDialogHiddenForThreeDays,
checkToggleText = stringResource(coreR.string.do_not_see_for_three_days),
leftButtonText = stringResource(coreR.string.next_time),
rightButtonText = stringResource(coreR.string.connect),
onLeftButtonClick = { viewModel.setEvent(TrainerHomeUiEvent.OnDismissDialog) },
onRightButtonClick = { viewModel.setEvent(TrainerHomeUiEvent.OnConfirmConnectDialog) },
onCheckClick = { viewModel.setEvent(TrainerHomeUiEvent.OnChangeHideDialogOption) },
onDismiss = { viewModel.setEvent(TrainerHomeUiEvent.OnDismissDialog) },
)
}

TrainerHomeUiState.DialogState.ADD_PT_CONNECT -> {
TnTPopupDialog(
title = "회원을 연결해 주세요",
content = "연결하지 않을 경우 수업을 추가할 수 없어요\n초대 코드를 복사해 연결해주시겠어요?",
leftButtonText = stringResource(coreR.string.next_time),
rightButtonText = stringResource(coreR.string.connect),
onLeftButtonClick = { viewModel.setEvent(TrainerHomeUiEvent.OnDismissDialog) },
onRightButtonClick = { viewModel.setEvent(TrainerHomeUiEvent.OnConfirmConnectDialog) },
onDismiss = { viewModel.setEvent(TrainerHomeUiEvent.OnDismissDialog) },
)
}
}

// TODO 홈 화면 진입 시마다 데이터 조회 재고 필요
LaunchedEffect(true) { viewModel.setEvent(TrainerHomeUiEvent.OnScreen) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,33 @@ package co.kr.tnt.trainer.home
import androidx.lifecycle.viewModelScope
import co.kr.tnt.domain.model.PtSession
import co.kr.tnt.domain.model.trainer.TrainerDailyPtSessionCount
import co.kr.tnt.domain.repository.ConnectRepository
import co.kr.tnt.domain.repository.TrainerRepository
import co.kr.tnt.trainer.home.TrainerHomeContract.TrainerHomeSideEffect
import co.kr.tnt.trainer.home.TrainerHomeContract.TrainerHomeUiEvent
import co.kr.tnt.trainer.home.TrainerHomeContract.TrainerHomeUiState
import co.kr.tnt.trainer.home.TrainerHomeContract.TrainerHomeUiState.DialogState
import co.kr.tnt.ui.base.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
import java.time.Duration
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.YearMonth
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
import javax.inject.Inject

const val DIALOG_HIDE_DURATION_HOURS = 72

@HiltViewModel
internal class TrainerHomeViewModel @Inject constructor(
private val trainerRepository: TrainerRepository,
private val connectRepository: ConnectRepository,
) :
BaseViewModel<TrainerHomeUiState, TrainerHomeUiEvent, TrainerHomeSideEffect>(TrainerHomeUiState()) {
private val cachedMonthlyPtSessionCounts: ConcurrentMap<YearMonth, List<TrainerDailyPtSessionCount>> =
Expand All @@ -38,8 +46,12 @@ internal class TrainerHomeViewModel @Inject constructor(
TrainerHomeUiEvent.OnClickNotification -> sendEffect(TrainerHomeSideEffect.NavigateToNotification)
is TrainerHomeUiEvent.OnChangeVisibleMonth -> handleChangeVisibleMonth(event.yearMonth)
is TrainerHomeUiEvent.OnClickDay -> selectDay(event.day)
TrainerHomeUiEvent.OnClickAddPtSession -> sendEffect(TrainerHomeSideEffect.NavigateToAddPtSession)
TrainerHomeUiEvent.OnClickAddPtSession -> showConnectDialog(false)

is TrainerHomeUiEvent.OnClickPtSessionComplete -> completePtSession(event.ptSession)
TrainerHomeUiEvent.OnChangeHideDialogOption -> toggleDialogHiddenState()
TrainerHomeUiEvent.OnConfirmConnectDialog -> handleDialogConfirm()
TrainerHomeUiEvent.OnDismissDialog -> dismissDialog()
}
}

Expand Down Expand Up @@ -130,5 +142,72 @@ internal class TrainerHomeViewModel @Inject constructor(
cachedMonthlyPtSessionCounts.clear()
cachedDailyPtSession.clear()
selectDay(currentState.selectedDay)
showConnectDialog(true)
}

private fun showConnectDialog(triggeredByHome: Boolean) {
val currentDateTime = LocalDateTime.now()

viewModelScope.launch {
runCatching {
trainerRepository.getActiveMembers()
}.onSuccess { result ->
if (result.isNotEmpty()) {
updateState { copy(dialogState = DialogState.NONE) }
if (triggeredByHome.not()) {
sendEffect(TrainerHomeSideEffect.NavigateToAddPtSession)
}
return@launch
}
}

val lastHiddenDate = connectRepository.getExplicitDeniedConnectDate().firstOrNull()
val isHidden = lastHiddenDate != null &&
Duration.between(lastHiddenDate, currentDateTime).toHours() < DIALOG_HIDE_DURATION_HOURS

if (isHidden.not() && triggeredByHome) {
updateState { copy(dialogState = DialogState.HOME_CONNECT) }
} else if (triggeredByHome.not()) {
updateState { copy(dialogState = DialogState.ADD_PT_CONNECT) }
}
}
}

private fun toggleDialogHiddenState() {
updateState { copy(isDialogHiddenForThreeDays = !isDialogHiddenForThreeDays) }
}

private fun handleDialogConfirm() {
if (currentState.isDialogHiddenForThreeDays) {
updateCurrentDateTime()
}
val effect = when (currentState.dialogState) {
DialogState.HOME_CONNECT -> TrainerHomeSideEffect.NavigateToInvite
DialogState.ADD_PT_CONNECT -> TrainerHomeSideEffect.NavigateToInvite
else -> return
}
updateState {
copy(dialogState = DialogState.NONE, isDialogHiddenForThreeDays = false)
}
sendEffect(effect)
}

private fun updateCurrentDateTime() {
val currentDateTime = LocalDateTime.now()
viewModelScope.launch {
connectRepository.updateExplicitDeniedConnectDate(currentDateTime)
}
}

private fun dismissDialog() {
if (currentState.isDialogHiddenForThreeDays) {
updateCurrentDateTime()
}
updateState {
copy(
dialogState = DialogState.NONE,
isDialogHiddenForThreeDays = false,
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ fun NavGraphBuilder.trainerHomeNavGraph(
padding: PaddingValues,
navigateToNotification: () -> Unit,
navigateToAddPtSession: () -> Unit,
navigateToInvite: (Boolean) -> Unit,
homeDestination: NavGraphBuilder.() -> Unit = { },
) {
navigation<Route.TrainerMainTab.Home>(startDestination = Route.TrainerHome) {
Expand All @@ -35,6 +36,7 @@ fun NavGraphBuilder.trainerHomeNavGraph(
padding = padding,
navigateToNotification = navigateToNotification,
navigateToAddPtSession = navigateToAddPtSession,
navigateToInvite = navigateToInvite,
)
}
homeDestination()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ private fun TrainerMainScreen(
padding = innerPadding,
navigateToNotification = navController::navigateToTrainerNotification,
navigateToAddPtSession = navController::navigateToAddPtSession,
navigateToInvite = navigateToInvite,
) {
trainerNotification(
navigateToPrevious = navController::safePopBackStack,
Expand Down