Skip to content

Commit

Permalink
SessionDetail 화면 개선
Browse files Browse the repository at this point in the history
- UIState 재설계 (UiState가 Domain의 모델을 가지고 있지 않도록 수정)
- AttendanceType을 UI가 아닌 SessionDetailViewModel 에서 연산하도록 함
  • Loading branch information
toastmeister1 committed May 4, 2024
1 parent cc77e0f commit cbc0967
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,9 @@ import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.google.accompanist.insets.systemBarsPadding
Expand All @@ -28,29 +25,21 @@ import com.yapp.common.yds.YDSAppBar
import com.yapp.common.yds.YDSAttendanceType
import com.yapp.common.yds.YDSEmptyScreen
import com.yapp.common.yds.YDSProgressBar
import com.yapp.domain.model.Session
import com.yapp.domain.util.DateUtil
import com.yapp.presentation.util.attendance.checkSessionAttendance
import com.yapp.common.yds.icon
import com.yapp.common.yds.text

@Composable
fun SessionDetail(
viewModel: SessionDetailViewModel = hiltViewModel(),
onClickBackButton: () -> Unit,
) {
val dateUtil = remember { DateUtil() }
val uiState by viewModel.uiState.collectAsState()
val session: Session? = uiState.session?.first
val attendance = checkSessionAttendance(
session = session!!,
attendance = uiState.session!!.second,
isPastSession = with(dateUtil) { currentTime isAfterFrom session.startTime }
)

Scaffold(
topBar = {
YDSAppBar(
modifier = Modifier.background(AttendanceTheme.colors.backgroundColors.background),
title = session.title,
title = uiState.appBarTitle,
onClickBackButton = onClickBackButton
)
},
Expand All @@ -64,8 +53,7 @@ fun SessionDetail(
SessionDetailContract.SessionDetailUiState.LoadState.Error -> YDSEmptyScreen()
SessionDetailContract.SessionDetailUiState.LoadState.Idle -> SessionDetailScreen(
modifier = Modifier.padding(contentPadding),
session = session,
attendance = attendance
state = uiState.screenState
)
}
}
Expand All @@ -74,68 +62,55 @@ fun SessionDetail(
@Composable
fun SessionDetailScreen(
modifier: Modifier = Modifier,
session: Session?,
attendance: YDSAttendanceType?
state: SessionDetailContract.SessionDetailUiState.SessionDetailScreenState
) {

Column(
modifier = modifier
.fillMaxSize()
.background(AttendanceTheme.colors.backgroundColors.background)
.padding(horizontal = 24.dp, vertical = 40.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth(),
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
if (attendance != null) {
if (attendance in listOf(
YDSAttendanceType.ABSENT,
YDSAttendanceType.ATTEND,
YDSAttendanceType.TARDY
)
) {
Icon(
painterResource(attendance.icon),
contentDescription = null,
tint = Color.Unspecified,
)
}
Text(
text = stringResource(id = attendance.title),
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.padding(horizontal = 4.dp),
color = when (attendance) {
YDSAttendanceType.ATTEND -> AttendanceTheme.colors.etcColors.EtcGreen
YDSAttendanceType.ABSENT -> AttendanceTheme.colors.etcColors.EtcRed
YDSAttendanceType.TARDY -> AttendanceTheme.colors.etcColors.EtcYellowFont
YDSAttendanceType.TBD, YDSAttendanceType.NO_ATTENDANCE, YDSAttendanceType.NO_YAPP -> AttendanceTheme.colors.grayScale.Gray400
}
)
}
if (session != null) {
Text(
text = session.monthAndDay,
style = AttendanceTypography.body1,
color = AttendanceTheme.colors.grayScale.Gray600
if (state.shouldShowIcon) {
Icon(
painter = state.attendanceType.icon(),
contentDescription = null,
tint = Color.Unspecified,
)
}

Text(
text = state.attendanceType.text(),
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.padding(horizontal = 4.dp),
color = when (state.attendanceType) {
YDSAttendanceType.ATTEND -> AttendanceTheme.colors.etcColors.EtcGreen
YDSAttendanceType.ABSENT -> AttendanceTheme.colors.etcColors.EtcRed
YDSAttendanceType.TARDY -> AttendanceTheme.colors.etcColors.EtcYellowFont
YDSAttendanceType.TBD, YDSAttendanceType.NO_ATTENDANCE, YDSAttendanceType.NO_YAPP -> AttendanceTheme.colors.grayScale.Gray400
}
)
Text(
text = state.date,
style = AttendanceTypography.body1,
color = AttendanceTheme.colors.grayScale.Gray600
)
}

Text(
text = session?.title ?: "",
text = state.title,
modifier = Modifier.padding(top = 28.dp),
style = AttendanceTypography.h1,
color = AttendanceTheme.colors.grayScale.Gray1000
)

Text(
text = session?.description ?: "",
text = state.description,
modifier = Modifier.padding(top = 12.dp),
style = AttendanceTypography.body1,
color = AttendanceTheme.colors.grayScale.Gray800
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,32 @@ package com.yapp.presentation.ui.member.score.detail
import com.yapp.common.base.UiEvent
import com.yapp.common.base.UiSideEffect
import com.yapp.common.base.UiState
import com.yapp.domain.model.Attendance
import com.yapp.domain.model.Session
import com.yapp.common.yds.YDSAttendanceType

class SessionDetailContract {
data class SessionDetailUiState(
val loadState: LoadState = LoadState.Idle,
val session: Pair<Session, Attendance>? = null,
val appBarTitle: String = "",
val screenState: SessionDetailScreenState = SessionDetailScreenState()
) : UiState {
enum class LoadState {
Loading, Idle, Error
}

data class SessionDetailScreenState(
val title: String = "",
val description: String = "",
val date: String = "",
val attendanceType: YDSAttendanceType = YDSAttendanceType.ABSENT
) {

val shouldShowIcon: Boolean
get() = attendanceType in showingIconTypes

private companion object {
val showingIconTypes = listOf(YDSAttendanceType.ABSENT, YDSAttendanceType.ATTEND, YDSAttendanceType.TARDY)
}
}
}

sealed class SessionDetailUiSideEffect : UiSideEffect {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,54 +3,74 @@ package com.yapp.presentation.ui.member.score.detail
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.yapp.common.base.BaseViewModel
import com.yapp.domain.usecases.GetCurrentTimeUseCase
import com.yapp.domain.usecases.GetMemberAttendanceListUseCase
import com.yapp.presentation.common.AttendanceTypeMapper
import com.yapp.presentation.ui.member.score.detail.SessionDetailContract.SessionDetailUiEvent
import com.yapp.presentation.ui.member.score.detail.SessionDetailContract.SessionDetailUiSideEffect
import com.yapp.presentation.ui.member.score.detail.SessionDetailContract.SessionDetailUiState
import com.yapp.presentation.ui.member.score.detail.SessionDetailContract.SessionDetailUiState.SessionDetailScreenState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject

@HiltViewModel
class SessionDetailViewModel @Inject constructor(
private val getMemberAttendanceListUseCase: GetMemberAttendanceListUseCase,
private val savedStateHandle: SavedStateHandle,
) : BaseViewModel<SessionDetailContract.SessionDetailUiState, SessionDetailContract.SessionDetailUiSideEffect, SessionDetailContract.SessionDetailUiEvent>(
SessionDetailContract.SessionDetailUiState()
) {
private val getCurrentTimeUseCase: GetCurrentTimeUseCase,
private val attendanceTypeMapper: AttendanceTypeMapper,
savedStateHandle: SavedStateHandle,
) : BaseViewModel<SessionDetailUiState, SessionDetailUiSideEffect, SessionDetailUiEvent>(SessionDetailUiState()) {

init {
setState { copy(loadState = SessionDetailContract.SessionDetailUiState.LoadState.Loading) }
setState { copy(loadState = SessionDetailUiState.LoadState.Loading) }
val sessionId = savedStateHandle.get<Int>("session")
val currentTime = getCurrentTimeUseCase()

if (sessionId != null) {
viewModelScope.launch {
withContext(Dispatchers.IO) {
getMemberAttendanceListUseCase().collect { result ->
getMemberAttendanceListUseCase().collectLatest { result ->
result.onSuccess { (sessions, attendances) ->
if (attendances.isEmpty()) {
setState { copy(loadState = SessionDetailContract.SessionDetailUiState.LoadState.Error) }
setState { copy(loadState = SessionDetailUiState.LoadState.Error) }
return@onSuccess
}

val session = sessions[sessionId]
val attendance = attendances[sessionId]

setState {
copy(
loadState = SessionDetailContract.SessionDetailUiState.LoadState.Idle,
session = sessions[sessionId] to attendances[sessionId]
loadState = SessionDetailUiState.LoadState.Idle,
appBarTitle = session.title,
screenState = SessionDetailScreenState(
title = session.title,
description = session.description,
date = session.monthAndDay,
attendanceType = attendanceTypeMapper.map(
sessionType = session.type,
attendanceStatus = attendance.status,
isPastSession = currentTime.isAfter(session.startTime)
)
)
)
}
}
.onFailure {
setState { copy(loadState = SessionDetailContract.SessionDetailUiState.LoadState.Error) }
setState { copy(loadState = SessionDetailUiState.LoadState.Error) }
}
}
}
}
} else {
setState { copy(loadState = SessionDetailContract.SessionDetailUiState.LoadState.Error) }
setState { copy(loadState = SessionDetailUiState.LoadState.Error) }
}
}

override suspend fun handleEvent(event: SessionDetailContract.SessionDetailUiEvent) {
TODO("Not yet implemented")
}
override suspend fun handleEvent(event: SessionDetailUiEvent) = Unit

}

0 comments on commit cbc0967

Please sign in to comment.