diff --git a/core/build.gradle.kts b/core/build.gradle.kts index eae55974c..84b342488 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -66,4 +66,7 @@ dependencies { testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) + + //ThirdPartyDependencies + implementation(libs.compose.coil) } \ No newline at end of file diff --git a/core/src/main/java/com/terning/core/designsystem/component/item/InternItem.kt b/core/src/main/java/com/terning/core/designsystem/component/item/InternItem.kt new file mode 100644 index 000000000..1e659b21d --- /dev/null +++ b/core/src/main/java/com/terning/core/designsystem/component/item/InternItem.kt @@ -0,0 +1,140 @@ +package com.terning.core.designsystem.component.item + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxHeight +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.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import coil.request.ImageRequest +import com.terning.core.R +import com.terning.core.designsystem.theme.Black +import com.terning.core.designsystem.theme.Grey300 +import com.terning.core.designsystem.theme.Grey400 +import com.terning.core.designsystem.theme.TerningMain +import com.terning.core.designsystem.theme.TerningPointTheme +import com.terning.core.designsystem.theme.TerningTheme +import com.terning.core.extension.noRippleClickable + +@Composable +fun InternItem( + imageUrl: String, + title: String, + dateDeadline: String, + workingPeriod: String, + isScraped: Boolean, + modifier: Modifier = Modifier, + scrapId: Int = 0, + onScrapButtonClicked: (Int) -> Unit = {}, +) { + Row( + modifier = modifier + .padding(10.dp) + .fillMaxSize(), + ) { + AsyncImage( + model = ImageRequest.Builder(LocalContext.current) + .data(imageUrl) + .build(), + contentDescription = title, + Modifier + .fillMaxHeight() + .aspectRatio(1f) + .clip(RoundedCornerShape(5.dp)) + .background(color = Grey300) + ) + Column( + modifier = Modifier + .padding(start = 8.dp) + .fillMaxHeight() + ) { + Text( + text = stringResource(id = R.string.intern_item_d_day, dateDeadline), + style = TerningTheme.typography.detail0, + color = TerningMain, + ) + + Text( + text = title, + style = TerningTheme.typography.title5, + color = Black, + softWrap = true, + modifier = modifier.padding(top = 3.dp), + ) + + Row( + modifier = Modifier.fillMaxHeight(), + verticalAlignment = Alignment.Bottom + ) { + Text( + text = stringResource(R.string.intern_item_working_period), + style = TerningTheme.typography.detail3, + color = Grey400 + ) + + Text( + text = stringResource(id = R.string.intern_item_working_period_month, workingPeriod), + style = TerningTheme.typography.detail3, + color = TerningMain, + modifier = modifier + .padding(start = 4.dp) + ) + } + } + + Box(modifier = Modifier.fillMaxSize()) { + Image( + painter = painterResource( + id = + if (isScraped) + R.drawable.ic_bookmark_filled + else + R.drawable.ic_bookmark_outlined + ), + contentDescription = stringResource(id = R.string.intern_item_scrap), + modifier = modifier + .align(Alignment.BottomEnd) + .noRippleClickable { + onScrapButtonClicked(scrapId) + }, + ) + } + } +} + +@Preview(showBackground = true) +@Composable +fun InternItemPreview() { + TerningPointTheme { + Box( + modifier = Modifier + .height(height = 92.dp) + .fillMaxWidth(), + ){ + InternItem( + imageUrl = "", + title = "test title", + dateDeadline = "3", + workingPeriod = "6", + isScraped = true + ) + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/terning/core/designsystem/component/box/ScrapBox.kt b/core/src/main/java/com/terning/core/designsystem/component/item/ScrapBox.kt similarity index 95% rename from core/src/main/java/com/terning/core/designsystem/component/box/ScrapBox.kt rename to core/src/main/java/com/terning/core/designsystem/component/item/ScrapBox.kt index 03c6eb7f7..a2f26072a 100644 --- a/core/src/main/java/com/terning/core/designsystem/component/box/ScrapBox.kt +++ b/core/src/main/java/com/terning/core/designsystem/component/item/ScrapBox.kt @@ -1,10 +1,8 @@ -package com.terning.core.designsystem.component.box +package com.terning.core.designsystem.component.item import androidx.compose.foundation.background import androidx.compose.foundation.border 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 diff --git a/feature/src/main/java/com/terning/feature/calendar/component/CalendarTopBar.kt b/core/src/main/java/com/terning/core/designsystem/component/topappbar/CalendarTopAppBar.kt similarity index 69% rename from feature/src/main/java/com/terning/feature/calendar/component/CalendarTopBar.kt rename to core/src/main/java/com/terning/core/designsystem/component/topappbar/CalendarTopAppBar.kt index bdfcf8ffd..a18a80fa6 100644 --- a/feature/src/main/java/com/terning/feature/calendar/component/CalendarTopBar.kt +++ b/core/src/main/java/com/terning/core/designsystem/component/topappbar/CalendarTopAppBar.kt @@ -1,4 +1,4 @@ -package com.terning.feature.calendar.component +package com.terning.core.designsystem.component.topappbar import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -15,19 +15,21 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.terning.core.R import com.terning.core.designsystem.theme.Black import com.terning.core.designsystem.theme.Grey300 +import com.terning.core.designsystem.theme.TerningMain import com.terning.core.designsystem.theme.TerningPointTheme import com.terning.core.designsystem.theme.TerningTheme import com.terning.core.extension.getStringAsTitle import com.terning.core.extension.noRippleClickable -import com.terning.feature.R import java.time.LocalDate @Composable -fun CalendarTopBar( +fun CalendarTopAppBar( modifier: Modifier = Modifier, date: LocalDate, + isWeekExpanded: Boolean, isListExpanded: Boolean, onListButtonClicked: () -> Unit, onMonthNavigationButtonClicked: (Int) -> Unit, @@ -47,24 +49,28 @@ fun CalendarTopBar( modifier = Modifier.align(Alignment.Center), verticalAlignment = Alignment.CenterVertically ) { - Icon( - painter = painterResource(id = R.drawable.ic_calendar_previous), - contentDescription = stringResource(id = R.string.calendar_button_description_previous), - tint = Grey300, - modifier = Modifier.noRippleClickable { onMonthNavigationButtonClicked(-1) } - ) + if(!isWeekExpanded || isListExpanded) { + Icon( + painter = painterResource(id = R.drawable.ic_calendar_previous), + contentDescription = stringResource(id = R.string.calendar_button_description_previous), + tint = Grey300, + modifier = Modifier.noRippleClickable { onMonthNavigationButtonClicked(-1) } + ) + } Text( text = date.getStringAsTitle(), style = TerningTheme.typography.title2, color = Black, modifier = Modifier.padding(horizontal = 8.dp) ) - Icon( - painter = painterResource(id = R.drawable.ic_calendar_next), - contentDescription = stringResource(id = R.string.calendar_button_description_next), - tint = Grey300, - modifier = Modifier.noRippleClickable { onMonthNavigationButtonClicked(1) } - ) + if(!isWeekExpanded || isListExpanded) { + Icon( + painter = painterResource(id = R.drawable.ic_calendar_next), + contentDescription = stringResource(id = R.string.calendar_button_description_next), + tint = Grey300, + modifier = Modifier.noRippleClickable { onMonthNavigationButtonClicked(1) } + ) + } } Box( modifier = Modifier @@ -80,7 +86,7 @@ fun CalendarTopBar( ), contentDescription = stringResource(id = R.string.calendar_button_description_list), modifier = Modifier.noRippleClickable { onListButtonClicked() }, - tint = if (isListExpanded) Color.Green else Color.LightGray, + tint = if (isListExpanded) TerningMain else Grey300, ) } } @@ -90,9 +96,11 @@ fun CalendarTopBar( @Composable fun CalendarTopBarPreview() { TerningPointTheme { - CalendarTopBar( + CalendarTopAppBar( date = LocalDate.now(), isListExpanded = false, + isWeekExpanded = false + , onListButtonClicked = {}, onMonthNavigationButtonClicked = {} ) diff --git a/core/src/main/java/com/terning/core/extension/LocalDateExt.kt b/core/src/main/java/com/terning/core/extension/LocalDateExt.kt index ee8c97476..702ea1182 100644 --- a/core/src/main/java/com/terning/core/extension/LocalDateExt.kt +++ b/core/src/main/java/com/terning/core/extension/LocalDateExt.kt @@ -1,10 +1,15 @@ package com.terning.core.extension import java.time.LocalDate +import java.time.format.TextStyle +import java.util.Locale fun LocalDate.getStringAsTitle(): String = "${year}년 ${monthValue.toString().padStart(2, '0')}월" -fun LocalDate.getWeekIndexContainingSelectedDate(): Int = dayOfMonth / 7 +fun LocalDate.getDateStringInKorean(): String = + "${monthValue}월 ${dayOfMonth}일 ${dayOfWeek.getDisplayName(TextStyle.FULL, Locale.KOREAN)}" + +fun LocalDate.getWeekIndexContainingSelectedDate(inDays: Int): Int = (inDays + dayOfMonth - 1) / 7 fun LocalDate.isToday(): Boolean = this == LocalDate.now() \ No newline at end of file diff --git a/core/src/main/res/drawable/ic_bookmark_filled.xml b/core/src/main/res/drawable/ic_bookmark_filled.xml new file mode 100644 index 000000000..0b884f362 --- /dev/null +++ b/core/src/main/res/drawable/ic_bookmark_filled.xml @@ -0,0 +1,13 @@ + + + diff --git a/core/src/main/res/drawable/ic_bookmark_outlined.xml b/core/src/main/res/drawable/ic_bookmark_outlined.xml new file mode 100644 index 000000000..ed8cf9471 --- /dev/null +++ b/core/src/main/res/drawable/ic_bookmark_outlined.xml @@ -0,0 +1,13 @@ + + + diff --git a/feature/src/main/res/drawable/ic_calendar_list_selected.xml b/core/src/main/res/drawable/ic_calendar_list_selected.xml similarity index 100% rename from feature/src/main/res/drawable/ic_calendar_list_selected.xml rename to core/src/main/res/drawable/ic_calendar_list_selected.xml diff --git a/feature/src/main/res/drawable/ic_calendar_list_unselected.xml b/core/src/main/res/drawable/ic_calendar_list_unselected.xml similarity index 100% rename from feature/src/main/res/drawable/ic_calendar_list_unselected.xml rename to core/src/main/res/drawable/ic_calendar_list_unselected.xml diff --git a/feature/src/main/res/drawable/ic_calendar_next.xml b/core/src/main/res/drawable/ic_calendar_next.xml similarity index 100% rename from feature/src/main/res/drawable/ic_calendar_next.xml rename to core/src/main/res/drawable/ic_calendar_next.xml diff --git a/feature/src/main/res/drawable/ic_calendar_previous.xml b/core/src/main/res/drawable/ic_calendar_previous.xml similarity index 100% rename from feature/src/main/res/drawable/ic_calendar_previous.xml rename to core/src/main/res/drawable/ic_calendar_previous.xml diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 3de74512c..b9a310626 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -11,6 +11,17 @@ button + + 근무기간 + %s개월 + D-%s + 스크랩 + + + previous + next + list + 채용 마감 이른순 짧은 근무 기간순 diff --git a/feature/src/main/java/com/terning/feature/calendar/CalendarRoute.kt b/feature/src/main/java/com/terning/feature/calendar/CalendarRoute.kt deleted file mode 100644 index 827f8d416..000000000 --- a/feature/src/main/java/com/terning/feature/calendar/CalendarRoute.kt +++ /dev/null @@ -1,154 +0,0 @@ -package com.terning.feature.calendar - -import androidx.activity.compose.BackHandler -import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.SizeTransform -import androidx.compose.animation.slideInVertically -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.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.foundation.lazy.rememberLazyListState -import androidx.compose.material3.Scaffold -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue -import androidx.compose.runtime.snapshotFlow -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.terning.core.designsystem.theme.Grey200 -import com.terning.feature.R -import com.terning.feature.calendar.component.CalendarTopBar -import com.terning.feature.calendar.component.WeekDaysHeader -import com.terning.feature.calendar.models.CalendarState -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.launch -import java.time.LocalDate - -@Composable -fun CalendarRoute() { - CalendarScreen() -} - -@Composable -fun CalendarScreen( - modifier: Modifier = Modifier, - viewModel: CalendarViewModel = hiltViewModel() -) { - val selectedDate by viewModel.selectedDate.collectAsStateWithLifecycle() - val state by remember { mutableStateOf(CalendarState()) } - - val listState = rememberLazyListState( - initialFirstVisibleItemIndex = state.getInitialPage() - ) - - var currentDate by remember { mutableStateOf(LocalDate.now()) } - var currentPage by remember { mutableIntStateOf(listState.firstVisibleItemIndex) } - - var isListExpanded by remember { mutableStateOf(false) } - - LaunchedEffect(key1 = listState) { - snapshotFlow { listState.firstVisibleItemIndex } - .distinctUntilChanged() - .collect { - val swipeDirection = - (listState.firstVisibleItemIndex - currentPage).coerceIn(-1, 1).toLong() - currentDate = currentDate.plusMonths(swipeDirection) - currentPage = listState.firstVisibleItemIndex - } - } - - BackHandler { - if (selectedDate.isEnabled) { - viewModel.updateSelectedDate(selectedDate.selectedDate) - } - } - - Scaffold( - modifier = modifier, - topBar = { - val coroutineScope = rememberCoroutineScope() - CalendarTopBar( - date = currentDate, - isListExpanded = isListExpanded, - onListButtonClicked = { isListExpanded = !isListExpanded }, - onMonthNavigationButtonClicked = { direction -> - coroutineScope.launch { - listState.animateScrollToItem( - index = listState.firstVisibleItemIndex + direction, - ) - } - } - ) - } - ) { paddingValues -> - Column( - modifier = Modifier - .fillMaxSize() - .padding(top = paddingValues.calculateTopPadding()) - ) { - WeekDaysHeader() - Spacer( - modifier = Modifier - .fillMaxWidth() - .height(1.dp) - .background(color = Grey200) - ) - - AnimatedContent( - targetState = selectedDate.isEnabled, - transitionSpec = { - if (!targetState) { - slideInVertically { fullHeight -> -fullHeight } togetherWith - slideOutVertically { fullHeight -> fullHeight } - } else { - slideInVertically { fullHeight -> fullHeight } togetherWith - slideOutVertically { fullHeight -> -fullHeight } - }.using( - sizeTransform = SizeTransform(clip = true) - ) - }, - label = stringResource(id = R.string.calendar_animation_label) - ) { targetState -> - if (!targetState) { - CalendarMonths( - modifier = Modifier.fillMaxSize(), - selectedDate = selectedDate, - onDateSelected = { - viewModel.updateSelectedDate(it) - }, - listState = listState, - pages = state.getPageCount(), - scrapLists = viewModel.mockScrapList, - ) - } else { - CalendarWeekWithScrap( - modifier = Modifier - .fillMaxSize(), - selectedDate = selectedDate, - scrapLists = viewModel.mockScrapList, - onDateSelected = { - viewModel.updateSelectedDate(it) - } - ) - } - } - } - } -} - - diff --git a/feature/src/main/java/com/terning/feature/calendar/CalendarDay.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarDay.kt similarity index 98% rename from feature/src/main/java/com/terning/feature/calendar/CalendarDay.kt rename to feature/src/main/java/com/terning/feature/calendar/calendar/CalendarDay.kt index fe4af64e2..14a3b8656 100644 --- a/feature/src/main/java/com/terning/feature/calendar/CalendarDay.kt +++ b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarDay.kt @@ -1,4 +1,4 @@ -package com.terning.feature.calendar +package com.terning.feature.calendar.calendar import androidx.compose.foundation.background import androidx.compose.foundation.clickable diff --git a/feature/src/main/java/com/terning/feature/calendar/CalendarMonth.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarMonth.kt similarity index 87% rename from feature/src/main/java/com/terning/feature/calendar/CalendarMonth.kt rename to feature/src/main/java/com/terning/feature/calendar/calendar/CalendarMonth.kt index fc9636da3..3d1822b5c 100644 --- a/feature/src/main/java/com/terning/feature/calendar/CalendarMonth.kt +++ b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarMonth.kt @@ -1,4 +1,4 @@ -package com.terning.feature.calendar +package com.terning.feature.calendar.calendar import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column @@ -8,6 +8,7 @@ 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.HorizontalDivider import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -19,6 +20,7 @@ import com.terning.core.extension.isToday import com.terning.feature.calendar.models.MonthData import com.terning.feature.calendar.models.Scrap import com.terning.feature.calendar.models.SelectedDateState +import com.terning.feature.calendar.scrap.CalendarScrapStrip import java.time.LocalDate import java.time.YearMonth @@ -54,8 +56,8 @@ fun CalendarMonth( onDateSelected = onDateSelected ) if(!day.isOutDate) { - val index = day.date.dayOfWeek.value - 1 - CalendarScrap( + val index = day.date.dayOfMonth - 1 + CalendarScrapStrip( scrapList = scrapLists[index] ) } @@ -63,11 +65,9 @@ fun CalendarMonth( } } if(month.indexOf(week) != month.lastIndex) { - Spacer( - modifier = Modifier - .fillMaxWidth() - .height(1.dp) - .background(color = Grey150) + HorizontalDivider( + thickness = 1.dp, + color = Grey150 ) } } diff --git a/feature/src/main/java/com/terning/feature/calendar/CalendarMonths.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarMonths.kt similarity index 97% rename from feature/src/main/java/com/terning/feature/calendar/CalendarMonths.kt rename to feature/src/main/java/com/terning/feature/calendar/calendar/CalendarMonths.kt index 5d4ecc101..5d6e9d6c3 100644 --- a/feature/src/main/java/com/terning/feature/calendar/CalendarMonths.kt +++ b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarMonths.kt @@ -1,4 +1,4 @@ -package com.terning.feature.calendar +package com.terning.feature.calendar.calendar import androidx.compose.foundation.background import androidx.compose.foundation.lazy.LazyListState diff --git a/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarRoute.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarRoute.kt new file mode 100644 index 000000000..7d8372b70 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarRoute.kt @@ -0,0 +1,188 @@ +package com.terning.feature.calendar.calendar + +import androidx.activity.compose.BackHandler +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.SizeTransform +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.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.foundation.lazy.rememberLazyListState +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Scaffold +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.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.terning.core.designsystem.component.topappbar.CalendarTopAppBar +import com.terning.core.designsystem.theme.Grey200 +import com.terning.feature.R +import com.terning.feature.calendar.calendar.component.WeekDaysHeader +import com.terning.feature.calendar.models.CalendarState +import com.terning.feature.calendar.scrap.CalendarScrapListScreen +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.launch +import java.time.LocalDate + +@Composable +fun CalendarRoute() { + CalendarScreen() +} + +@Composable +fun CalendarScreen( + modifier: Modifier = Modifier, + viewModel: CalendarViewModel = hiltViewModel() +) { + val selectedDate by viewModel.selectedDate.collectAsStateWithLifecycle() + val state by remember { mutableStateOf(CalendarState()) } + + val listState = rememberLazyListState( + initialFirstVisibleItemIndex = state.getInitialPage() + ) + + var currentDate by remember { mutableStateOf(LocalDate.now()) } + var currentPage by remember { mutableIntStateOf(listState.firstVisibleItemIndex) } + + var isListExpanded by remember { mutableStateOf(false) } + + LaunchedEffect(key1 = listState) { + snapshotFlow { listState.firstVisibleItemIndex } + .distinctUntilChanged() + .collect { + val swipeDirection = + (listState.firstVisibleItemIndex - currentPage).coerceIn(-1, 1).toLong() + currentDate = currentDate.plusMonths(swipeDirection) + currentPage = listState.firstVisibleItemIndex + } + } + + BackHandler { + if (selectedDate.isEnabled) { + viewModel.updateSelectedDate(selectedDate.selectedDate) + } + } + + Scaffold( + modifier = modifier, + topBar = { + val coroutineScope = rememberCoroutineScope() + CalendarTopAppBar( + date = currentDate, + isListExpanded = isListExpanded, + isWeekExpanded = selectedDate.isEnabled, + onListButtonClicked = { + isListExpanded = !isListExpanded + if(selectedDate.isEnabled){ + viewModel.disableWeekCalendar() + } }, + onMonthNavigationButtonClicked = { direction -> + coroutineScope.launch { + listState.animateScrollToItem( + index = listState.firstVisibleItemIndex + direction, + ) + } + } + ) + } + ) { paddingValues -> + AnimatedContent( + targetState = isListExpanded, + transitionSpec = { + if (!targetState) { + slideInHorizontally { fullWidth -> -fullWidth } togetherWith + slideOutHorizontally { fullWidth -> fullWidth } + } else { + slideInHorizontally { fullWidth -> fullWidth } togetherWith + slideOutHorizontally { fullWidth -> -fullWidth } + }.using( + sizeTransform = SizeTransform(clip = true) + ) + }, + label = stringResource(id = R.string.calendar_animation_label) + ) { isListExpanded -> + if (isListExpanded) { + CalendarScrapListScreen( + modifier = Modifier + .padding(top = paddingValues.calculateTopPadding()), + scrapList = viewModel.mockScrapList, + listState = listState, + pages = state.getPageCount(), + ) + } else { + Column( + modifier = Modifier + .fillMaxSize() + .padding(top = paddingValues.calculateTopPadding()) + ) { + WeekDaysHeader() + + HorizontalDivider( + thickness = 1.dp, + color = Grey200 + ) + + AnimatedContent( + targetState = selectedDate.isEnabled, + transitionSpec = { + if (!targetState) { + slideInVertically { fullHeight -> -fullHeight } togetherWith + slideOutVertically { fullHeight -> fullHeight } + } else { + slideInVertically { fullHeight -> fullHeight } togetherWith + slideOutVertically { fullHeight -> -fullHeight } + }.using( + sizeTransform = SizeTransform(clip = true) + ) + }, + label = stringResource(id = R.string.calendar_animation_label) + ) { targetState -> + if (!targetState) { + CalendarMonths( + modifier = Modifier.fillMaxSize(), + selectedDate = selectedDate, + onDateSelected = { + viewModel.updateSelectedDate(it) + }, + listState = listState, + pages = state.getPageCount(), + scrapLists = viewModel.mockScrapList, + ) + } else { + CalendarWeekWithScrap( + modifier = Modifier + .fillMaxSize(), + selectedDate = selectedDate, + scrapLists = viewModel.mockScrapList, + onDateSelected = {newDate-> + viewModel.updateSelectedDate(newDate) + } + ) + } + } + } + } + } + + } +} + + diff --git a/feature/src/main/java/com/terning/feature/calendar/CalendarViewModel.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarViewModel.kt similarity index 71% rename from feature/src/main/java/com/terning/feature/calendar/CalendarViewModel.kt rename to feature/src/main/java/com/terning/feature/calendar/calendar/CalendarViewModel.kt index 6a286807b..49f263050 100644 --- a/feature/src/main/java/com/terning/feature/calendar/CalendarViewModel.kt +++ b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarViewModel.kt @@ -1,4 +1,4 @@ -package com.terning.feature.calendar +package com.terning.feature.calendar.calendar import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -41,13 +41,22 @@ class CalendarViewModel @Inject constructor( } else { _selectedDate.update { currentState -> currentState.copy( - selectedDate = date, - isEnabled = !_selectedDate.value.isEnabled + isEnabled = !currentState.isEnabled ) } } } + fun disableWeekCalendar() { + _selectedDate.update { currentState -> + currentState.copy( + isEnabled = false + ) + } + } + + + //To be erased in future val mockScrapList: List> get() { @@ -65,7 +74,7 @@ class CalendarViewModel @Inject constructor( list.add( i, listOf( - Scrap("Task1_1", CalBlue1), + Scrap("Task1_1", CalBlue1, dDay = "1", period = "3", isScraped = true), ) ) } @@ -74,8 +83,8 @@ class CalendarViewModel @Inject constructor( list.add( i, listOf( - Scrap("Task2_1", CalPink), - Scrap("Task2_2", CalGreen1) + Scrap("Task2_1", CalPink, dDay = "2", period = "3", isScraped = true), + Scrap("Task2_2", CalGreen1, dDay = "2", period = "3", isScraped = true) ) ) } @@ -98,12 +107,12 @@ class CalendarViewModel @Inject constructor( list.add( i, listOf( - Scrap("Task3_1", CalPink), - Scrap("Task3_2", CalPurple), - Scrap("Task3_3", CalRed), - Scrap("Task3_4", CalBlue1), - Scrap("Task3_5", CalGreen2), - Scrap("Task3_6", CalYellow) + Scrap("Task3_1", CalPink, dDay = "5", period = "3", isScraped = true), + Scrap("Task3_2", CalPurple, dDay = "5", period = "3", isScraped = true), + Scrap("Task3_3", CalRed, dDay = "5", period = "3", isScraped = true), + Scrap("Task3_4", CalBlue1, dDay = "5", period = "3", isScraped = true), + Scrap("Task3_5", CalGreen2, dDay = "5", period = "3", isScraped = true), + Scrap("Task3_6", CalYellow, dDay = "5", period = "3", isScraped = true) ) ) } diff --git a/feature/src/main/java/com/terning/feature/calendar/CalendarWeek.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarWeek.kt similarity index 87% rename from feature/src/main/java/com/terning/feature/calendar/CalendarWeek.kt rename to feature/src/main/java/com/terning/feature/calendar/calendar/CalendarWeek.kt index 7fb0e5d43..ea6bb76b6 100644 --- a/feature/src/main/java/com/terning/feature/calendar/CalendarWeek.kt +++ b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarWeek.kt @@ -1,4 +1,4 @@ -package com.terning.feature.calendar +package com.terning.feature.calendar.calendar import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.fillMaxWidth @@ -13,19 +13,21 @@ import androidx.compose.ui.unit.dp import com.terning.core.extension.getWeekIndexContainingSelectedDate import com.terning.core.extension.isToday import com.terning.feature.calendar.models.MonthData +import com.terning.feature.calendar.models.Scrap import com.terning.feature.calendar.models.SelectedDateState +import timber.log.Timber import java.time.LocalDate import java.time.YearMonth @Composable fun CalendarWeek( - modifier: Modifier = Modifier, selectedDate: SelectedDateState, + modifier: Modifier = Modifier, onDateSelected: (LocalDate) -> Unit = {} ) { val date = selectedDate.selectedDate val monthData = MonthData(YearMonth.of(date.year, date.monthValue)) - val currentWeek = date.getWeekIndexContainingSelectedDate() + val currentWeek = date.getWeekIndexContainingSelectedDate(monthData.inDays) val pagerState = rememberPagerState ( initialPage = currentWeek, @@ -38,7 +40,7 @@ fun CalendarWeek( LazyRow( modifier = Modifier .fillMaxWidth() - .padding(20.dp), + .padding(horizontal = 32.dp, vertical = 20.dp), horizontalArrangement = Arrangement.SpaceBetween ) { items(items = monthData.calendarMonth.weekDays[page]) { day -> diff --git a/feature/src/main/java/com/terning/feature/calendar/CalendarWeekWithScrap.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarWeekWithScrap.kt similarity index 60% rename from feature/src/main/java/com/terning/feature/calendar/CalendarWeekWithScrap.kt rename to feature/src/main/java/com/terning/feature/calendar/calendar/CalendarWeekWithScrap.kt index fbdf774cc..54ab943b3 100644 --- a/feature/src/main/java/com/terning/feature/calendar/CalendarWeekWithScrap.kt +++ b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarWeekWithScrap.kt @@ -1,20 +1,28 @@ -package com.terning.feature.calendar +package com.terning.feature.calendar.calendar import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Card +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import com.terning.core.designsystem.theme.Back import com.terning.core.designsystem.theme.Grey200 +import com.terning.core.designsystem.theme.Grey400 +import com.terning.core.designsystem.theme.TerningTheme import com.terning.core.designsystem.theme.White +import com.terning.feature.R import com.terning.feature.calendar.models.Scrap import com.terning.feature.calendar.models.SelectedDateState +import com.terning.feature.calendar.scrap.CalendarScrapList import java.time.LocalDate @Composable @@ -26,13 +34,15 @@ fun CalendarWeekWithScrap( ) { Column( modifier = modifier + .background(Back) ) { Card( modifier = Modifier .border( width = 0.dp, color = Grey200, - shape = RoundedCornerShape(bottomStart = 20.dp, bottomEnd = 20.dp)) + shape = RoundedCornerShape(bottomStart = 20.dp, bottomEnd = 20.dp) + ) .shadow( shape = RoundedCornerShape(bottomStart = 20.dp, bottomEnd = 20.dp), elevation = 1.dp @@ -48,16 +58,23 @@ fun CalendarWeekWithScrap( onDateSelected = onDateSelected ) } + CalendarScrapList( + selectedDate = selectedDate.selectedDate, + scrapLists = scrapLists, + noScrapScreen = { + Text( + modifier = Modifier + .padding(top = 42.dp) + .fillMaxWidth(), + text = stringResource(id = R.string.calendar_empty_scrap), + textAlign = TextAlign.Center, + style = TerningTheme.typography.body5, + color = Grey400 + ) + } + ) + } +} - LazyColumn( - modifier = Modifier - .fillMaxWidth() - ) { - /*items(items = scrapLists[selectedDate?.dayOfMonth - 1]) { - - }*/ - } - } -} \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/calendar/component/WeekDaysHeader.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/component/WeekDaysHeader.kt similarity index 97% rename from feature/src/main/java/com/terning/feature/calendar/component/WeekDaysHeader.kt rename to feature/src/main/java/com/terning/feature/calendar/calendar/component/WeekDaysHeader.kt index 098459ce0..e02bde2ce 100644 --- a/feature/src/main/java/com/terning/feature/calendar/component/WeekDaysHeader.kt +++ b/feature/src/main/java/com/terning/feature/calendar/calendar/component/WeekDaysHeader.kt @@ -1,4 +1,4 @@ -package com.terning.feature.calendar.component +package com.terning.feature.calendar.calendar.component import androidx.compose.foundation.background import androidx.compose.foundation.layout.Row diff --git a/feature/src/main/java/com/terning/feature/calendar/models/Scrap.kt b/feature/src/main/java/com/terning/feature/calendar/models/Scrap.kt index 73698591a..86d00350e 100644 --- a/feature/src/main/java/com/terning/feature/calendar/models/Scrap.kt +++ b/feature/src/main/java/com/terning/feature/calendar/models/Scrap.kt @@ -6,5 +6,9 @@ import androidx.compose.ui.graphics.Color @Immutable data class Scrap( val text: String, - val backgroundColor: Color + val backgroundColor: Color, + val dDay: String, + val period: String, + val isScraped: Boolean = true, + val image: String? = null ) diff --git a/feature/src/main/java/com/terning/feature/calendar/navigation/CalendarNavigation.kt b/feature/src/main/java/com/terning/feature/calendar/navigation/CalendarNavigation.kt index 542b876d8..1725d9eb7 100644 --- a/feature/src/main/java/com/terning/feature/calendar/navigation/CalendarNavigation.kt +++ b/feature/src/main/java/com/terning/feature/calendar/navigation/CalendarNavigation.kt @@ -5,7 +5,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable import com.terning.core.navigation.MainTabRoute -import com.terning.feature.calendar.CalendarRoute +import com.terning.feature.calendar.calendar.CalendarRoute import kotlinx.serialization.Serializable diff --git a/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrap.kt b/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrap.kt new file mode 100644 index 000000000..d89c1e9b2 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrap.kt @@ -0,0 +1,52 @@ +package com.terning.feature.calendar.scrap + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.terning.core.designsystem.component.item.InternItem +import com.terning.core.designsystem.component.item.ScrapBox +import com.terning.core.designsystem.theme.CalBlue1 +import com.terning.core.designsystem.theme.TerningPointTheme +import com.terning.feature.calendar.models.Scrap + +@Composable +fun CalendarScrap( + scrap: Scrap, + modifier: Modifier = Modifier, +) { + ScrapBox( + cornerRadius = 10.dp, + scrapColor = scrap.backgroundColor, + elevation = 1.dp, + modifier = modifier + .height(height = 92.dp) + .fillMaxWidth(), + ) { + InternItem( + imageUrl = scrap.image.orEmpty(), + title = scrap.text, + dateDeadline = scrap.dDay, + workingPeriod = scrap.period, + isScraped = scrap.isScraped + ) + } +} + +@Preview(showBackground = true) +@Composable +fun CalendarScrapPreview() { + TerningPointTheme { + CalendarScrap( + scrap = Scrap( + text = "Task1_1", + backgroundColor = CalBlue1, + dDay = "1", + period = "3", + isScraped = true + ) + ) + } +} \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrapList.kt b/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrapList.kt new file mode 100644 index 000000000..d1a9dc1ff --- /dev/null +++ b/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrapList.kt @@ -0,0 +1,63 @@ +package com.terning.feature.calendar.scrap + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.terning.core.designsystem.theme.Black +import com.terning.core.designsystem.theme.TerningTheme +import com.terning.core.extension.getDateStringInKorean +import com.terning.feature.calendar.models.Scrap +import java.time.LocalDate + +@Composable +fun CalendarScrapList( + selectedDate: LocalDate, + scrapLists: List>, + isFromList: Boolean = false, + noScrapScreen: @Composable () -> Unit +) { + val scrollState = rememberScrollState() + if (scrapLists[selectedDate.dayOfMonth - 1].isNotEmpty()) { + Text( + text = selectedDate.getDateStringInKorean(), + style = TerningTheme.typography.title5, + color = Black, + modifier = Modifier.padding(start = 24.dp, top = 16.dp, bottom = 15.dp) + ) + } + val topModifier = if (!isFromList) { + Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp) + .verticalScroll(scrollState) + } else { + Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp) + } + + if (scrapLists[selectedDate.dayOfMonth - 1].isEmpty()) { + noScrapScreen() + } else { + Column( + modifier = topModifier + ) { + for (scrap in scrapLists[selectedDate.dayOfMonth - 1]) { + CalendarScrap( + scrap = scrap + ) + Spacer( + modifier = Modifier.height(12.dp) + ) + } + } + } +} \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrapListScreen.kt b/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrapListScreen.kt new file mode 100644 index 000000000..1731b789b --- /dev/null +++ b/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrapListScreen.kt @@ -0,0 +1,70 @@ +package com.terning.feature.calendar.scrap + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.terning.core.designsystem.theme.Back +import com.terning.core.designsystem.theme.Grey200 +import com.terning.core.designsystem.theme.White +import com.terning.feature.calendar.models.CalendarDefaults.flingBehavior +import com.terning.feature.calendar.models.CalendarState.Companion.getDateByPage +import com.terning.feature.calendar.models.Scrap +import java.time.LocalDate + +@Composable +fun CalendarScrapListScreen( + scrapList: List>, + pages: Int, + listState: LazyListState, + modifier: Modifier = Modifier +) { + LazyRow( + modifier = modifier + .background(White), + state = listState, + userScrollEnabled = true, + flingBehavior = flingBehavior( + state = listState + ) + ) { + items(pages) { page -> + val getDate = getDateByPage(page) + + LazyColumn( + modifier = Modifier + .fillParentMaxWidth() + .fillMaxHeight() + .background(Back) + ) { + items(scrapList.size) { day -> + val currentDate = LocalDate.of(getDate.year, getDate.monthValue, day + 1) + CalendarScrapList( + selectedDate = currentDate, + scrapLists = scrapList, + isFromList = true, + noScrapScreen = {}) + + + if (scrapList[day].isNotEmpty()) { + Spacer( + modifier = Modifier + .height(4.dp) + .fillMaxWidth() + .background(Grey200) + ) + } + } + } + } + } + +} + diff --git a/feature/src/main/java/com/terning/feature/calendar/CalendarScrap.kt b/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrapStrip.kt similarity index 94% rename from feature/src/main/java/com/terning/feature/calendar/CalendarScrap.kt rename to feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrapStrip.kt index a28d47c19..34e8bdcf3 100644 --- a/feature/src/main/java/com/terning/feature/calendar/CalendarScrap.kt +++ b/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrapStrip.kt @@ -1,4 +1,4 @@ -package com.terning.feature.calendar +package com.terning.feature.calendar.scrap import androidx.compose.foundation.background import androidx.compose.foundation.layout.fillMaxWidth @@ -16,7 +16,7 @@ import com.terning.core.designsystem.theme.White import com.terning.feature.calendar.models.Scrap @Composable -fun CalendarScrap( +fun CalendarScrapStrip( modifier: Modifier = Modifier, scrapList: List ) { @@ -47,9 +47,8 @@ fun CalendarScrap( ) } } - } } -private const val MAX_SCRAP_COUNT = 2 +private const val MAX_SCRAP_COUNT = 3 diff --git a/feature/src/main/res/values/strings.xml b/feature/src/main/res/values/strings.xml index 50b4c07a1..694b1ee2f 100644 --- a/feature/src/main/res/values/strings.xml +++ b/feature/src/main/res/values/strings.xml @@ -52,6 +52,7 @@ next list Calendar Transition + 선택하신 날짜에 지원 마감인 스크랩 공고가 없어요 오늘 마감되는 %s님의 관심 공고 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 86c865a76..1606804dd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -56,7 +56,7 @@ coroutine = "1.8.1" timber = "5.0.1" ## Coil -coil = "1.4.0" +coil = "2.4.0" ## Security securityVersion = "1.1.0-alpha06"