Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TNT-180] 트레이니 주간 캘린더 연동 #67

Merged
merged 9 commits into from
Feb 8, 2025
18 changes: 18 additions & 0 deletions core/designsystem/src/main/res/drawable/ic_alarm.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M5.202,17.5H18.798C19.844,17.5 20.551,16.434 20.146,15.471L19.701,14.415C19.238,13.316 19,12.136 19,10.944V9C19,5.134 15.866,2 12,2C8.134,2 5,5.134 5,9V10.944C5,12.136 4.762,13.316 4.299,14.415L3.854,15.471C3.449,16.434 4.156,17.5 5.202,17.5Z"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#262626"/>
<path
android:pathData="M9,18C9,18 9,21 12,21C15,21 15,18 15,18"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#262626"
android:strokeLineCap="round"/>
</vector>
12 changes: 12 additions & 0 deletions domain/src/main/java/co/kr/tnt/domain/model/RecordList.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package co.kr.tnt.domain.model

import java.time.LocalDate

data class RecordList(
val recordDate: LocalDate,
val recordType: RecordType,
val recordTime: String,
val recordImage: String?,
val recordContents: String,
val hasFeedback: Boolean,
)
1 change: 1 addition & 0 deletions feature/trainee/home/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ android {

dependencies {
implementation(libs.kotlinx.immutable)
implementation(libs.calendar.compose)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package co.kr.tnt.trainee.home

import co.kr.tnt.domain.model.RecordList
import co.kr.tnt.ui.base.UiEvent
import co.kr.tnt.ui.base.UiSideEffect
import co.kr.tnt.ui.base.UiState
import java.time.LocalDate
import java.time.YearMonth

class TraineeHomeContract {
data class TraineeHomeUiState(
val visibleYearMonth: YearMonth = YearMonth.now(),
val selectedDate: LocalDate = LocalDate.now(),
val markedDates: List<LocalDate> = emptyList(),
val recordList: List<RecordList> = emptyList(),
) : UiState

sealed interface TraineeHomeUiEvent : UiEvent {
data object OnNextWeekClick : TraineeHomeUiEvent
data object OnPreviousWeekClick : TraineeHomeUiEvent
data class OnDayClick(val date: LocalDate) : TraineeHomeUiEvent
}

sealed interface TraineeHomeEffect : UiSideEffect {
data class ShowToast(val message: String) : TraineeHomeEffect
}
}
Original file line number Diff line number Diff line change
@@ -1,37 +1,161 @@
package co.kr.tnt.trainee.home

import android.widget.Toast
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.kr.tnt.designsystem.component.calendar.TnTCalendarSelector
import co.kr.tnt.designsystem.component.calendar.TnTIndicatorWeekCalendar
import co.kr.tnt.designsystem.component.calendar.model.DayIndicatorState
import co.kr.tnt.designsystem.component.calendar.model.DayState
import co.kr.tnt.designsystem.theme.TnTTheme
import co.kr.tnt.trainee.home.TraineeHomeContract.TraineeHomeUiEvent
import co.kr.tnt.trainee.home.TraineeHomeContract.TraineeHomeUiState
import com.kizitonwose.calendar.compose.weekcalendar.rememberWeekCalendarState
import kotlinx.coroutines.launch
import java.time.LocalDate
import java.time.YearMonth
import co.kr.tnt.core.designsystem.R as uiResource

@Composable
@Suppress("UnusedParameter")
internal fun TraineeHomeRoute(
viewModel: TraineeHomeViewModel = hiltViewModel(),
navigateToNotification: () -> Unit,
) {
TraineeHomeScreen(navigateToNotification)
val context = LocalContext.current
val uiState by viewModel.uiState.collectAsStateWithLifecycle()

TraineeHomeScreen(
state = uiState,
navigateToNotification = navigateToNotification,
onSelectDate = { date ->
viewModel.setEvent(TraineeHomeUiEvent.OnDayClick(date))
},
onNextWeekClick = { viewModel.setEvent(TraineeHomeUiEvent.OnNextWeekClick) },
onPreviousWeekClick = { viewModel.setEvent(TraineeHomeUiEvent.OnPreviousWeekClick) },
)

LaunchedEffect(viewModel.effect) {
viewModel.effect.collect { effect ->
when (effect) {
is TraineeHomeContract.TraineeHomeEffect.ShowToast -> {
Toast.makeText(context, effect.message, Toast.LENGTH_SHORT).show()
}
}
}
}
}

@Composable
fun TraineeHomeScreen(
private fun TraineeHomeScreen(
state: TraineeHomeUiState,
onSelectDate: (LocalDate) -> Unit,
onNextWeekClick: () -> Unit,
onPreviousWeekClick: () -> Unit,
navigateToNotification: () -> Unit,
) {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Column {
Text(
text = "trainee home",
modifier = Modifier.padding(innerPadding),
)
Button(onClick = navigateToNotification) {
Text("navigate to notification")
val now = LocalDate.now()
val coroutineScope = rememberCoroutineScope()

val weekCalendarState = rememberWeekCalendarState(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우리 앱의 첫번째 요일은 '일요일'이므로 별도로 FirstDayOfWeek 선언해주는게 좋을 것 같습니다 ~

startDate = now.minusWeeks(6),
endDate = now.plusWeeks(6),
firstVisibleWeekDate = state.selectedDate,
)

Scaffold(
containerColor = TnTTheme.colors.commonColors.Common0,
modifier = Modifier.fillMaxSize(),
) { innerPadding ->
Column(modifier = Modifier.padding(innerPadding)) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp, vertical = 8.dp),
) {
Spacer(Modifier.height(8.dp))
TnTCalendarSelector(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요거 제 브랜치에서 TnTHomeTopBar 만들어뒀어요! 체리픽해서 사용하면 조을 것 같습니당

yearMonth = state.visibleYearMonth,
onClickPrevious = {
coroutineScope.launch {
onPreviousWeekClick()
weekCalendarState.animateScrollToWeek(state.selectedDate)
}
},
onClickNext = {
coroutineScope.launch {
onNextWeekClick()
weekCalendarState.animateScrollToWeek(state.selectedDate)
}
},
modifier = Modifier.align(Alignment.Center),
)
IconButton(
onClick = navigateToNotification,
modifier = Modifier.align(Alignment.CenterEnd),
) {
Icon(
painter = painterResource(uiResource.drawable.ic_alarm),
contentDescription = "alarm",
)
}
}
Spacer(modifier = Modifier.height(16.dp))
TnTIndicatorWeekCalendar(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

최소 날짜, 최대 날짜 지정이 필요할 것 같습니다!

state = weekCalendarState,
dayState = { date ->
DayState(isSelected = date == state.selectedDate)
},
indicatorState = { date ->
DayIndicatorState(showIcon = date in state.markedDates)
},
onClickDay = { date ->
onSelectDate(date)
},
)
Spacer(modifier = Modifier.height(12.dp))
}
}
}

@Preview
@Composable
private fun TraineeHomeScreenPreview() {
val now = LocalDate.now()

val dummyUiState = TraineeHomeUiState(
selectedDate = now,
visibleYearMonth = YearMonth.from(now),
markedDates = List(5) { now.minusDays(it.toLong() * 2) },
)

TnTTheme {
TraineeHomeScreen(
state = dummyUiState,
navigateToNotification = { },
onSelectDate = {},
onNextWeekClick = { },
onPreviousWeekClick = { },
)
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,54 @@
package co.kr.tnt.trainee.home

import androidx.lifecycle.ViewModel
import co.kr.tnt.trainee.home.TraineeHomeContract.TraineeHomeEffect
import co.kr.tnt.trainee.home.TraineeHomeContract.TraineeHomeUiEvent
import co.kr.tnt.trainee.home.TraineeHomeContract.TraineeHomeUiState
import co.kr.tnt.ui.base.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import java.time.LocalDate
import java.time.YearMonth
import javax.inject.Inject

@HiltViewModel
class TraineeHomeViewModel @Inject constructor() : ViewModel()
internal class TraineeHomeViewModel @Inject constructor() :
BaseViewModel<TraineeHomeUiState, TraineeHomeUiEvent, TraineeHomeEffect>(
TraineeHomeUiState(),
) {
init {
updateCalenderState()
}

override suspend fun handleEvent(event: TraineeHomeUiEvent) {
when (event) {
TraineeHomeUiEvent.OnNextWeekClick -> moveToNextWeek()
TraineeHomeUiEvent.OnPreviousWeekClick -> moveToPreviousWeek()
is TraineeHomeUiEvent.OnDayClick -> selectDate(event.date)
}
}

// TODO : 주간 캘린더 API 연동
private fun updateCalenderState() {
val today = LocalDate.now()
val list = List(10) {
today.minusDays((0..30).random().toLong())
}
updateState { copy(markedDates = list) }
}

private fun selectDate(date: LocalDate) {
updateState {
copy(
selectedDate = date,
visibleYearMonth = YearMonth.from(date),
)
}
}

private fun moveToNextWeek() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7일 중 과반 이상(4개 이상)이 해당하는 년도-월로 표시

현재 보이는 '연월'은 위 기획과 동일하게 설정되도록 수정이 필요할 것 같아요!

selectDate(currentState.selectedDate.plusWeeks(1))
}

private fun moveToPreviousWeek() {
selectDate(currentState.selectedDate.minusWeeks(1))
}
}