Skip to content

Commit

Permalink
[MERGE] #305 -> develop
Browse files Browse the repository at this point in the history
[UI/#305] ์บ˜๋ฆฐํ„ฐ ํ„ฐ์น˜ ์˜์—ญ ์ˆ˜์ •
  • Loading branch information
boiledEgg-s authored Dec 21, 2024
2 parents 1a90ef2 + 2743ab1 commit fec7a5c
Show file tree
Hide file tree
Showing 28 changed files with 1,036 additions and 674 deletions.
Original file line number Diff line number Diff line change
@@ -1,38 +1,37 @@
package com.terning.feature.calendar.calendar

import androidx.compose.animation.core.Transition
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.terning.core.analytics.EventType
import com.terning.core.analytics.LocalTracker
import com.terning.core.designsystem.component.topappbar.CalendarTopAppBar
import com.terning.core.designsystem.extension.getWeekIndexContainingSelectedDate
import com.terning.core.designsystem.theme.Grey200
import com.terning.core.designsystem.theme.White
import com.terning.feature.calendar.calendar.component.ScreenTransition
import com.terning.feature.calendar.calendar.component.WeekDaysHeader
import com.terning.feature.calendar.calendar.model.CalendarModel.Companion.getLocalDateByPage
import com.terning.feature.calendar.calendar.model.CalendarModel.Companion.getYearMonthByPage
import com.terning.feature.calendar.calendar.model.CalendarUiState
import com.terning.feature.calendar.calendar.model.LocalPagerState
import com.terning.feature.calendar.calendar.model.DayModel
import com.terning.feature.calendar.calendar.model.TerningCalendarModel
import com.terning.feature.calendar.list.CalendarListRoute
import com.terning.feature.calendar.month.CalendarMonthRoute
import com.terning.feature.calendar.week.CalendarWeekRoute
Expand All @@ -45,12 +44,10 @@ fun CalendarRoute(
modifier: Modifier = Modifier,
viewModel: CalendarViewModel = hiltViewModel()
) {
val lifecycleOwner = LocalLifecycleOwner.current
val uiState by viewModel.uiState.collectAsStateWithLifecycle(lifecycleOwner = lifecycleOwner)
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val amplitudeTracker = LocalTracker.current

CalendarScreen(
modifier = modifier,
uiState = uiState,
navigateToAnnouncement = navigateToAnnouncement,
onClickNewDate = viewModel::onSelectNewDate,
Expand All @@ -65,23 +62,29 @@ fun CalendarRoute(
)
}
viewModel.updateListVisibility(!uiState.isListEnabled)
}
},
modifier = modifier,
)
}

@Composable
private fun CalendarScreen(
uiState: CalendarUiState,
navigateToAnnouncement: (Long) -> Unit,
onClickNewDate: (LocalDate) -> Unit,
updateSelectedDate: (LocalDate) -> Unit,
onClickNewDate: (DayModel) -> Unit,
updateSelectedDate: (DayModel) -> Unit,
disableListVisibility: () -> Unit,
disableWeekVisibility: () -> Unit,
onClickListButton: () -> Unit,
modifier: Modifier = Modifier,
) {
val coroutineScope = rememberCoroutineScope()

val calendarListTransition =
updateTransition(!uiState.isListEnabled, label = "calendarListTransition")
val monthWeekTransition =
updateTransition(!uiState.isWeekEnabled, label = "monthWeekTransition")

val pagerState = rememberPagerState(
initialPage = uiState.calendarModel.initialPage,
pageCount = { uiState.calendarModel.pageCount }
Expand All @@ -90,95 +93,148 @@ private fun CalendarScreen(
LaunchedEffect(key1 = pagerState, key2 = uiState.selectedDate) {
snapshotFlow { pagerState.currentPage }
.collect { current ->
val date = getLocalDateByPage(current)
val date = uiState.calendarModel.getLocalDateByPage(current)
val month = uiState.calendarModel.getMonthModelByPage(current)

val newDate = LocalDate.of(
date.year,
date.month,
uiState.selectedDate.dayOfMonth.coerceAtMost(date.month.minLength())
uiState.selectedDate.date.dayOfMonth.coerceAtMost(date.month.minLength())
)
updateSelectedDate(newDate)

val currentWeek = newDate.getWeekIndexContainingSelectedDate(month.inDays)
updateSelectedDate(DayModel(newDate, currentWeek))
}
}

CompositionLocalProvider(
LocalPagerState provides pagerState

Column(
modifier = modifier,
) {
Column(
modifier = modifier,
) {
CalendarTopAppBar(
date = getYearMonthByPage(pagerState.settledPage),
isListExpanded = uiState.isListEnabled,
onListButtonClicked = onClickListButton,
onMonthNavigationButtonClicked = { direction ->
coroutineScope.launch {
pagerState.animateScrollToPage(
page = pagerState.settledPage + direction,
animationSpec = tween(500)
)
}
CalendarTopAppBar(
date = uiState.calendarModel.getYearMonthByPage(pagerState.settledPage),
isListExpanded = uiState.isListEnabled,
onListButtonClicked = {
if(!calendarListTransition.isRunning)
onClickListButton()
},
onMonthNavigationButtonClicked = { direction ->
coroutineScope.launch {
pagerState.animateScrollToPage(
page = pagerState.settledPage + direction,
animationSpec = tween(500)
)
}
)
ScreenTransition(
targetState = !uiState.isListEnabled,
transitionOne = slideInHorizontally { fullWidth -> -fullWidth } togetherWith
slideOutHorizontally { fullWidth -> fullWidth },
transitionTwo = slideInHorizontally { fullWidth -> fullWidth } togetherWith
slideOutHorizontally { fullWidth -> -fullWidth },
contentOne = {
Column(
modifier = Modifier
.fillMaxSize()
) {
WeekDaysHeader()

HorizontalDivider(
thickness = 1.dp,
color = Grey200
)

ScreenTransition(
targetState = !uiState.isWeekEnabled,
transitionOne = slideInVertically { fullHeight -> -fullHeight } togetherWith
slideOutVertically { fullHeight -> fullHeight },
transitionTwo = slideInVertically { fullHeight -> fullHeight } togetherWith
slideOutVertically { fullHeight -> -fullHeight },
contentOne = {
CalendarMonthRoute(
selectedDate = uiState.selectedDate,
updateSelectedDate = { newDate ->
if (!pagerState.isScrollInProgress)
onClickNewDate(newDate)
},
modifier = Modifier
.fillMaxSize()
.background(White),
)
},
contentTwo = {
CalendarWeekRoute(
calendarUiState = uiState,
modifier = Modifier
.fillMaxSize(),
navigateUp = disableWeekVisibility,
navigateToAnnouncement = navigateToAnnouncement,
updateSelectedDate = onClickNewDate
)
}
)
}
},
contentTwo = {
CalendarListRoute(
navigateToAnnouncement = navigateToAnnouncement,
navigateUp = disableListVisibility,
modifier = Modifier
.fillMaxSize()
}
)

CalendarListTransition(
transition = calendarListTransition,
calendarModel = uiState.calendarModel,
pagerState = pagerState,
onNavigateToAnnouncement = navigateToAnnouncement,
onNavigateUpToCalendar = disableListVisibility,
calendarContent = {
Column(
modifier = Modifier
.fillMaxSize()
) {
WeekDaysHeader()

HorizontalDivider(
thickness = 1.dp,
color = Grey200
)

MonthWeekTransition(
transition = monthWeekTransition,
selectedDate = uiState.selectedDate,
calendarModel = uiState.calendarModel,
pagerState = pagerState,
onSelectDate = { newDate -> onClickNewDate(newDate) },
onNavigateToAnnouncement = navigateToAnnouncement,
onNavigateUpToMonth = disableWeekVisibility
)
}
}
)
}
}


/** ๋‹ฌ๋ ฅ <-> ๋ชฉ๋ก ์ „ํ™˜ ์ปดํฌ์ €๋ธ” */
@Composable
private fun CalendarListTransition(
transition: Transition<Boolean>,
calendarModel: TerningCalendarModel,
pagerState: PagerState,
onNavigateToAnnouncement: (Long) -> Unit,
onNavigateUpToCalendar: () -> Unit,
calendarContent: @Composable () -> Unit,
) {
ScreenTransition(
transition = transition,
transitionOne = slideInHorizontally { fullWidth -> -fullWidth } togetherWith
slideOutHorizontally { fullWidth -> fullWidth },
transitionTwo = slideInHorizontally { fullWidth -> fullWidth } togetherWith
slideOutHorizontally { fullWidth -> -fullWidth },
contentOne = {
calendarContent()
},
contentTwo = {
CalendarListRoute(
calendarModel = calendarModel,
navigateToAnnouncement = onNavigateToAnnouncement,
navigateUp = onNavigateUpToCalendar,
pagerState = pagerState,
modifier = Modifier
.fillMaxSize()
)
},
)
}

/**์›”๊ฐ„ <-> ์ฃผ๊ฐ„ ์ „ํ™˜ ์ปดํฌ์ €๋ธ”*/
@Composable
private fun MonthWeekTransition(
transition: Transition<Boolean>,
selectedDate: DayModel,
calendarModel: TerningCalendarModel,
pagerState: PagerState,
onSelectDate: (DayModel) -> Unit,
onNavigateToAnnouncement: (Long) -> Unit,
onNavigateUpToMonth: () -> Unit,
) {
ScreenTransition(
transition = transition,
transitionOne = slideInVertically { fullHeight -> -fullHeight } togetherWith
slideOutVertically { fullHeight -> fullHeight },
transitionTwo = slideInVertically { fullHeight -> fullHeight } togetherWith
slideOutVertically { fullHeight -> -fullHeight },
contentOne = {
CalendarMonthRoute(
selectedDate = selectedDate,
updateSelectedDate = { newDate ->
if (!pagerState.isScrollInProgress)
onSelectDate(newDate)
},
pagerState = pagerState,
calendarModel = calendarModel
)
},
contentTwo = {
CalendarWeekRoute(
modifier = Modifier
.fillMaxSize(),
navigateUp = onNavigateUpToMonth,
navigateToAnnouncement = onNavigateToAnnouncement,
updateSelectedDate = onSelectDate,
selectedDate = selectedDate,
calendarModel = calendarModel,
pagerState = pagerState,
)
}
}
)
}


Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
package com.terning.feature.calendar.calendar

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.terning.feature.calendar.calendar.model.CalendarUiState
import com.terning.feature.calendar.calendar.model.DayModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import java.time.LocalDate
import javax.inject.Inject

@HiltViewModel
class CalendarViewModel @Inject constructor() : ViewModel() {
private var _uiState: MutableStateFlow<CalendarUiState> = MutableStateFlow(CalendarUiState())
val uiState get() = _uiState.asStateFlow()

fun onSelectNewDate(selectedDate: LocalDate) = viewModelScope.launch {
if (_uiState.value.selectedDate == selectedDate) {
_uiState.update { currentState ->
currentState.copy(
isWeekEnabled = !_uiState.value.isWeekEnabled
)
}
fun onSelectNewDate(selectedDate: DayModel) {
if (_uiState.value.selectedDate.date == selectedDate.date) {
updateWeekVisibility(!_uiState.value.isWeekEnabled)
} else {
_uiState.update { currentState ->
currentState.copy(
Expand All @@ -33,27 +27,23 @@ class CalendarViewModel @Inject constructor() : ViewModel() {
}
}

fun updateSelectedDate(date: LocalDate) = viewModelScope.launch {
_uiState.update { currentState ->
fun updateSelectedDate(value: DayModel) = _uiState.update { currentState ->
currentState.copy(
selectedDate = date
selectedDate = value
)
}
}

fun updateListVisibility(visibility: Boolean) = viewModelScope.launch {
_uiState.update { currentState ->

fun updateListVisibility(value: Boolean) = _uiState.update { currentState ->
currentState.copy(
isListEnabled = visibility
isListEnabled = value
)
}
}

fun updateWeekVisibility(visibility: Boolean) = viewModelScope.launch {
_uiState.update { currentState ->

fun updateWeekVisibility(value: Boolean) = _uiState.update { currentState ->
currentState.copy(
isWeekEnabled = visibility
isWeekEnabled = value
)
}
}
}
Loading

0 comments on commit fec7a5c

Please sign in to comment.