diff --git a/app/src/main/java/com/terning/point/di/DataSourceModule.kt b/app/src/main/java/com/terning/point/di/DataSourceModule.kt index 7e00cbb4c..f3b8cc071 100644 --- a/app/src/main/java/com/terning/point/di/DataSourceModule.kt +++ b/app/src/main/java/com/terning/point/di/DataSourceModule.kt @@ -5,6 +5,8 @@ import com.terning.data.datasource.SearchDataSource import com.terning.data.datasource.TokenReissueDataSource import com.terning.data.datasourceimpl.AuthDataSourceImpl import com.terning.data.datasourceimpl.SearchDataSourceImpl +import com.terning.data.datasource.CalendarDataSource +import com.terning.data.datasourceimpl.CalendarDataSourceImpl import com.terning.data.datasourceimpl.TokenReissueDataSourceImpl import dagger.Binds import dagger.Module @@ -16,6 +18,10 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) abstract class DataSourceModule { + @Binds + @Singleton + abstract fun bindCalendarDataSource(calendarDataSourceImpl: CalendarDataSourceImpl): CalendarDataSource + @Binds @Singleton abstract fun bindAuthDataSource(authDataSourceImpl: AuthDataSourceImpl): AuthDataSource diff --git a/app/src/main/java/com/terning/point/di/RepositoryModule.kt b/app/src/main/java/com/terning/point/di/RepositoryModule.kt index 2377e8ef7..34cda7a82 100644 --- a/app/src/main/java/com/terning/point/di/RepositoryModule.kt +++ b/app/src/main/java/com/terning/point/di/RepositoryModule.kt @@ -1,10 +1,12 @@ package com.terning.point.di import com.terning.data.repositoryimpl.AuthRepositoryImpl +import com.terning.data.repositoryimpl.CalendarRepositoryImpl import com.terning.data.repositoryimpl.SearchViewsRepositoryImpl import com.terning.data.repositoryimpl.TokenReissueRepositoryImpl import com.terning.data.repositoryimpl.TokenRepositoryImpl import com.terning.domain.repository.AuthRepository +import com.terning.domain.repository.CalendarRepository import com.terning.domain.repository.SearchRepository import com.terning.domain.repository.TokenReissueRepository import com.terning.domain.repository.TokenRepository @@ -18,6 +20,10 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) abstract class RepositoryModule { + @Binds + @Singleton + abstract fun calendarRepository(calendarRepositoryImpl: CalendarRepositoryImpl): CalendarRepository + @Binds @Singleton abstract fun bindAuthRepository(authRepositoryImpl: AuthRepositoryImpl): AuthRepository diff --git a/app/src/main/java/com/terning/point/di/ServiceModule.kt b/app/src/main/java/com/terning/point/di/ServiceModule.kt index 92c4765db..57edf477f 100644 --- a/app/src/main/java/com/terning/point/di/ServiceModule.kt +++ b/app/src/main/java/com/terning/point/di/ServiceModule.kt @@ -1,6 +1,7 @@ package com.terning.point.di import com.terning.data.service.AuthService +import com.terning.data.service.CalendarService import com.terning.data.service.SearchService import com.terning.data.service.TokenReissueService import com.terning.point.di.qualifier.JWT @@ -26,6 +27,12 @@ object ServiceModule { fun provideSearchService(@JWT retrofit: Retrofit): SearchService = retrofit.create(SearchService::class.java) + @Provides + @Singleton + fun provideCalendarService(@JWT retrofit: Retrofit): CalendarService = + retrofit.create(CalendarService::class.java) + + @Provides @Singleton fun provideTokenReissueService(@REISSUE retrofit: Retrofit): TokenReissueService = diff --git a/core/src/main/java/com/terning/core/designsystem/component/topappbar/CalendarTopAppBar.kt b/core/src/main/java/com/terning/core/designsystem/component/topappbar/CalendarTopAppBar.kt index a18a80fa6..2e95a0549 100644 --- a/core/src/main/java/com/terning/core/designsystem/component/topappbar/CalendarTopAppBar.kt +++ b/core/src/main/java/com/terning/core/designsystem/component/topappbar/CalendarTopAppBar.kt @@ -24,11 +24,12 @@ import com.terning.core.designsystem.theme.TerningTheme import com.terning.core.extension.getStringAsTitle import com.terning.core.extension.noRippleClickable import java.time.LocalDate +import java.time.YearMonth @Composable fun CalendarTopAppBar( modifier: Modifier = Modifier, - date: LocalDate, + date: YearMonth, isWeekExpanded: Boolean, isListExpanded: Boolean, onListButtonClicked: () -> Unit, @@ -58,7 +59,7 @@ fun CalendarTopAppBar( ) } Text( - text = date.getStringAsTitle(), + text = LocalDate.of(date.year, date.month, 1).getStringAsTitle(), style = TerningTheme.typography.title2, color = Black, modifier = Modifier.padding(horizontal = 8.dp) @@ -97,7 +98,7 @@ fun CalendarTopAppBar( fun CalendarTopBarPreview() { TerningPointTheme { CalendarTopAppBar( - date = LocalDate.now(), + date = YearMonth.now(), isListExpanded = false, isWeekExpanded = false , 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 702ea1182..1f8d4355f 100644 --- a/core/src/main/java/com/terning/core/extension/LocalDateExt.kt +++ b/core/src/main/java/com/terning/core/extension/LocalDateExt.kt @@ -1,6 +1,7 @@ package com.terning.core.extension import java.time.LocalDate +import java.time.format.DateTimeFormatter import java.time.format.TextStyle import java.util.Locale @@ -10,6 +11,11 @@ fun LocalDate.getStringAsTitle(): String = fun LocalDate.getDateStringInKorean(): String = "${monthValue}월 ${dayOfMonth}일 ${dayOfWeek.getDisplayName(TextStyle.FULL, Locale.KOREAN)}" +fun LocalDate.getDateAsMapString(): String { + val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") + return format(formatter) +} + 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/java/com/terning/core/extension/Map.kt b/core/src/main/java/com/terning/core/extension/Map.kt new file mode 100644 index 000000000..fab779e23 --- /dev/null +++ b/core/src/main/java/com/terning/core/extension/Map.kt @@ -0,0 +1,3 @@ +package com.terning.core.extension + +fun List?.isListNotEmpty():Boolean = this.orEmpty().isNotEmpty() diff --git a/data/src/main/java/com/terning/data/datasource/CalendarDataSource.kt b/data/src/main/java/com/terning/data/datasource/CalendarDataSource.kt new file mode 100644 index 000000000..609019fa8 --- /dev/null +++ b/data/src/main/java/com/terning/data/datasource/CalendarDataSource.kt @@ -0,0 +1,15 @@ +package com.terning.data.datasource + +import com.terning.data.dto.BaseResponse +import com.terning.data.dto.request.CalendarDayListRequestDto +import com.terning.data.dto.request.CalendarMonthListRequestDto +import com.terning.data.dto.request.CalendarMonthRequestDto +import com.terning.data.dto.response.CalendarDayListResponseDto +import com.terning.data.dto.response.CalendarMonthListResponseDto +import com.terning.data.dto.response.CalendarMonthResponseDto + +interface CalendarDataSource { + suspend fun getCalendarMonth(request: CalendarMonthRequestDto): BaseResponse> + suspend fun getCalendarMonthList(request: CalendarMonthListRequestDto): BaseResponse> + suspend fun getCalendarDayList(request: CalendarDayListRequestDto): BaseResponse> +} \ No newline at end of file diff --git a/data/src/main/java/com/terning/data/datasourceimpl/CalendarDataSourceImpl.kt b/data/src/main/java/com/terning/data/datasourceimpl/CalendarDataSourceImpl.kt new file mode 100644 index 000000000..15f60843e --- /dev/null +++ b/data/src/main/java/com/terning/data/datasourceimpl/CalendarDataSourceImpl.kt @@ -0,0 +1,25 @@ +package com.terning.data.datasourceimpl + +import com.terning.data.datasource.CalendarDataSource +import com.terning.data.dto.BaseResponse +import com.terning.data.dto.request.CalendarDayListRequestDto +import com.terning.data.dto.request.CalendarMonthListRequestDto +import com.terning.data.dto.request.CalendarMonthRequestDto +import com.terning.data.dto.response.CalendarDayListResponseDto +import com.terning.data.dto.response.CalendarMonthListResponseDto +import com.terning.data.dto.response.CalendarMonthResponseDto +import com.terning.data.service.CalendarService +import javax.inject.Inject + +class CalendarDataSourceImpl @Inject constructor( + private val calendarService: CalendarService +) : CalendarDataSource { + override suspend fun getCalendarMonth(request: CalendarMonthRequestDto): BaseResponse> = + calendarService.getCalendarScrapMonth(request.year, request.month) + + override suspend fun getCalendarMonthList(request: CalendarMonthListRequestDto): BaseResponse> = + calendarService.getCalendarScrapMonthList(request.year, request.month) + + override suspend fun getCalendarDayList(request: CalendarDayListRequestDto): BaseResponse> = + calendarService.getCalendarScrapDayList(request.date) +} \ No newline at end of file diff --git a/data/src/main/java/com/terning/data/dto/request/CalendarDayListRequestDto.kt b/data/src/main/java/com/terning/data/dto/request/CalendarDayListRequestDto.kt new file mode 100644 index 000000000..857b75029 --- /dev/null +++ b/data/src/main/java/com/terning/data/dto/request/CalendarDayListRequestDto.kt @@ -0,0 +1,5 @@ +package com.terning.data.dto.request + +data class CalendarDayListRequestDto( + val date: String +) \ No newline at end of file diff --git a/data/src/main/java/com/terning/data/dto/request/CalendarMonthListRequestDto.kt b/data/src/main/java/com/terning/data/dto/request/CalendarMonthListRequestDto.kt new file mode 100644 index 000000000..6a951d73d --- /dev/null +++ b/data/src/main/java/com/terning/data/dto/request/CalendarMonthListRequestDto.kt @@ -0,0 +1,13 @@ +package com.terning.data.dto.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CalendarMonthListRequestDto( + @SerialName("deadline") + val year: Int, + @SerialName("scraps") + val month: Int +) + diff --git a/data/src/main/java/com/terning/data/dto/request/CalendarMonthRequestDto.kt b/data/src/main/java/com/terning/data/dto/request/CalendarMonthRequestDto.kt new file mode 100644 index 000000000..00eaf4696 --- /dev/null +++ b/data/src/main/java/com/terning/data/dto/request/CalendarMonthRequestDto.kt @@ -0,0 +1,12 @@ +package com.terning.data.dto.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CalendarMonthRequestDto( + @SerialName("year") + val year: Int, + @SerialName("month") + val month: Int +) diff --git a/data/src/main/java/com/terning/data/dto/response/CalendarDayListResponseDto.kt b/data/src/main/java/com/terning/data/dto/response/CalendarDayListResponseDto.kt new file mode 100644 index 000000000..e2d23afb1 --- /dev/null +++ b/data/src/main/java/com/terning/data/dto/response/CalendarDayListResponseDto.kt @@ -0,0 +1,42 @@ +package com.terning.data.dto.response + +import com.terning.domain.entity.response.CalendarScrapDetailModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CalendarDayListResponseDto( + @SerialName("scrapId") + val scrapId: Long, + @SerialName("internshipAnnouncementId") + val internshipAnnouncementId: Long, + @SerialName("title") + val title: String, + @SerialName("dDay") + val dDay: String, + @SerialName("workingPeriod") + val workingPeriod: String, + @SerialName("color") + val color: String, + @SerialName("companyImage") + val companyImage: String, + @SerialName("startYear") + val startYear: Int, + @SerialName("startMonth") + val startMonth: Int +){ + fun toScrapDetailModelList(): CalendarScrapDetailModel = + CalendarScrapDetailModel( + scrapId = scrapId, + internshipAnnouncementId = internshipAnnouncementId, + title = title, + dDay = dDay, + workingPeriod = workingPeriod, + color = color, + companyImage = companyImage, + startYear = startYear, + startMonth = startMonth, + deadLine = "" + ) + +} \ No newline at end of file diff --git a/data/src/main/java/com/terning/data/dto/response/CalendarMonthListResponseDto.kt b/data/src/main/java/com/terning/data/dto/response/CalendarMonthListResponseDto.kt new file mode 100644 index 000000000..1aa7ac882 --- /dev/null +++ b/data/src/main/java/com/terning/data/dto/response/CalendarMonthListResponseDto.kt @@ -0,0 +1,50 @@ +package com.terning.data.dto.response + +import com.terning.domain.entity.response.CalendarScrapDetailModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CalendarMonthListResponseDto( + @SerialName("deadline") + val deadline: String, + @SerialName("scraps") + val scraps: List +) { + @Serializable + data class Scrap( + @SerialName("scrapId") + val scrapId: Long, + @SerialName("internshipAnnouncementId") + val internshipAnnouncementId: Long, + @SerialName("title") + val title: String, + @SerialName("dDay") + val dDay: String, + @SerialName("workingPeriod") + val workingPeriod: String, + @SerialName("color") + val color: String, + @SerialName("companyImage") + val companyImage: String, + @SerialName("startYear") + val startYear: Int, + @SerialName("startMonth") + val startMonth: Int + ) + + fun toScrapDetailModelList(): List = scraps.map { scrap -> + CalendarScrapDetailModel( + scrapId = scrap.scrapId, + internshipAnnouncementId = scrap.internshipAnnouncementId, + title = scrap.title, + dDay = scrap.dDay, + workingPeriod = scrap.workingPeriod, + color = scrap.color, + companyImage = scrap.companyImage, + startYear = scrap.startYear, + startMonth = scrap.startMonth, + deadLine = deadline + ) + } +} \ No newline at end of file diff --git a/data/src/main/java/com/terning/data/dto/response/CalendarMonthResponseDto.kt b/data/src/main/java/com/terning/data/dto/response/CalendarMonthResponseDto.kt new file mode 100644 index 000000000..2af310d95 --- /dev/null +++ b/data/src/main/java/com/terning/data/dto/response/CalendarMonthResponseDto.kt @@ -0,0 +1,33 @@ +package com.terning.data.dto.response + +import com.terning.domain.entity.response.CalendarScrapModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CalendarMonthResponseDto( + @SerialName("deadline") + val deadline: String, + @SerialName("scraps") + val scraps: List +) { + @Serializable + data class Scrap( + @SerialName("scrapId") + val scrapId: Long, + @SerialName("title") + val title: String, + @SerialName("color") + val color: String + ) + + fun toScrapModelList(): List = scraps.map { scrap -> + CalendarScrapModel( + scrapId = scrap.scrapId, + title = scrap.title, + color = scrap.color, + deadLine = deadline, + isScrapped = true + ) + } +} \ No newline at end of file diff --git a/data/src/main/java/com/terning/data/repositoryimpl/CalendarRepositoryImpl.kt b/data/src/main/java/com/terning/data/repositoryimpl/CalendarRepositoryImpl.kt new file mode 100644 index 000000000..b9e0c308f --- /dev/null +++ b/data/src/main/java/com/terning/data/repositoryimpl/CalendarRepositoryImpl.kt @@ -0,0 +1,67 @@ +package com.terning.data.repositoryimpl + +import com.terning.data.datasource.CalendarDataSource +import com.terning.data.dto.request.CalendarDayListRequestDto +import com.terning.data.dto.request.CalendarMonthListRequestDto +import com.terning.data.dto.request.CalendarMonthRequestDto +import com.terning.domain.entity.response.CalendarScrapDetailModel +import com.terning.domain.entity.response.CalendarScrapModel +import com.terning.domain.repository.CalendarRepository +import java.time.LocalDate +import java.time.format.DateTimeFormatter +import javax.inject.Inject + +class CalendarRepositoryImpl @Inject constructor( + private val calendarDataSource: CalendarDataSource +) : CalendarRepository { + override suspend fun getScrapMonth( + year: Int, + month: Int + ): Result>> = + runCatching { + val result = calendarDataSource.getCalendarMonth( + request = CalendarMonthRequestDto( + year = year, + month = month + ) + ).result + + val scrapModelMapByDeadLine = result.flatMap { dto -> + dto.toScrapModelList() + }.groupBy { it.deadLine } + + scrapModelMapByDeadLine + } + + override suspend fun getScrapMonthList( + year: Int, + month: Int + ): Result>> = + runCatching { + val result = calendarDataSource.getCalendarMonthList( + request = CalendarMonthListRequestDto( + year = year, + month = month + ) + ).result + + val scrapModelMapByDeadLine = result.flatMap { dto -> + dto.toScrapDetailModelList() + }.groupBy { it.deadLine } + + scrapModelMapByDeadLine + } + + override suspend fun getScrapDayList( + currentDate: LocalDate + ): Result> = + runCatching { + val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") + val request = CalendarDayListRequestDto(currentDate.format(formatter)) + val response = calendarDataSource.getCalendarDayList(request) + val scrapModelList = response.result.map { scrap -> + scrap.toScrapDetailModelList() + } + scrapModelList + } +} diff --git a/data/src/main/java/com/terning/data/service/CalendarService.kt b/data/src/main/java/com/terning/data/service/CalendarService.kt new file mode 100644 index 000000000..8ca05f7e9 --- /dev/null +++ b/data/src/main/java/com/terning/data/service/CalendarService.kt @@ -0,0 +1,27 @@ +package com.terning.data.service + +import com.terning.data.dto.BaseResponse +import com.terning.data.dto.response.CalendarDayListResponseDto +import com.terning.data.dto.response.CalendarMonthListResponseDto +import com.terning.data.dto.response.CalendarMonthResponseDto +import retrofit2.http.GET +import retrofit2.http.Query + +interface CalendarService{ + @GET("/api/v1/calendar/monthly-default") + suspend fun getCalendarScrapMonth( + @Query("year") year: Int, + @Query("month") month: Int + ): BaseResponse> + + @GET("/api/v1/calendar/monthly-list") + suspend fun getCalendarScrapMonthList( + @Query("year") year: Int, + @Query("month") month: Int + ): BaseResponse> + + @GET("/api/v1/calendar/daily") + suspend fun getCalendarScrapDayList( + @Query("date") date: String + ): BaseResponse> +} \ No newline at end of file diff --git a/domain/src/main/java/com/terning/domain/entity/response/CalendarScrapDetailModel.kt b/domain/src/main/java/com/terning/domain/entity/response/CalendarScrapDetailModel.kt new file mode 100644 index 000000000..881c4623b --- /dev/null +++ b/domain/src/main/java/com/terning/domain/entity/response/CalendarScrapDetailModel.kt @@ -0,0 +1,15 @@ +package com.terning.domain.entity.response + +data class CalendarScrapDetailModel( + val scrapId: Long, + val internshipAnnouncementId: Long, + val title: String, + val dDay: String, + val workingPeriod: String, + val color: String, + val companyImage: String, + val startYear: Int, + val startMonth: Int, + val deadLine: String, + val isScrapped: Boolean = true +) diff --git a/domain/src/main/java/com/terning/domain/entity/response/CalendarScrapModel.kt b/domain/src/main/java/com/terning/domain/entity/response/CalendarScrapModel.kt new file mode 100644 index 000000000..162b4544d --- /dev/null +++ b/domain/src/main/java/com/terning/domain/entity/response/CalendarScrapModel.kt @@ -0,0 +1,9 @@ +package com.terning.domain.entity.response + +data class CalendarScrapModel( + val scrapId: Long, + val title: String, + val deadLine: String, + val color: String, + val isScrapped: Boolean +) \ No newline at end of file diff --git a/domain/src/main/java/com/terning/domain/repository/CalendarRepository.kt b/domain/src/main/java/com/terning/domain/repository/CalendarRepository.kt new file mode 100644 index 000000000..2150e6864 --- /dev/null +++ b/domain/src/main/java/com/terning/domain/repository/CalendarRepository.kt @@ -0,0 +1,11 @@ +package com.terning.domain.repository + +import com.terning.domain.entity.response.CalendarScrapDetailModel +import com.terning.domain.entity.response.CalendarScrapModel +import java.time.LocalDate + +interface CalendarRepository{ + suspend fun getScrapMonth(year: Int, month: Int): Result>> + suspend fun getScrapMonthList(year: Int, month: Int): Result>> + suspend fun getScrapDayList(currentDate: LocalDate): Result> +} \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarMonths.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarMonths.kt deleted file mode 100644 index 5d6e9d6c3..000000000 --- a/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarMonths.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.terning.feature.calendar.calendar - -import androidx.compose.foundation.background -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -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.MonthData -import com.terning.feature.calendar.models.Scrap -import com.terning.feature.calendar.models.SelectedDateState -import java.time.LocalDate -import java.time.YearMonth - -@Composable -fun CalendarMonths( - listState: LazyListState, - onDateSelected: (LocalDate) -> Unit, - pages: Int, - selectedDate: SelectedDateState, - scrapLists: List>, - modifier: Modifier = Modifier, -) { - LazyRow( - modifier = modifier - .background(White), - state = listState, - userScrollEnabled = true, - flingBehavior = flingBehavior( - state = listState - ) - ) { - items(pages) { page -> - val date = getDateByPage(page) - val monthData = MonthData(YearMonth.of(date.year, date.month)) - - CalendarMonth( - modifier = Modifier.fillParentMaxSize(), - selectedDate = selectedDate, - onDateSelected = onDateSelected, - monthData = monthData, - scrapLists = scrapLists - ) - } - } -} \ No newline at end of file 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 index 7d8372b70..f0016d96d 100644 --- a/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarRoute.kt +++ b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarRoute.kt @@ -1,19 +1,13 @@ 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 @@ -28,42 +22,64 @@ 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.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.flowWithLifecycle +import androidx.navigation.NavController +import androidx.navigation.compose.rememberNavController import com.terning.core.designsystem.component.topappbar.CalendarTopAppBar import com.terning.core.designsystem.theme.Grey200 -import com.terning.feature.R +import com.terning.core.extension.toast +import com.terning.feature.calendar.calendar.component.ScreenTransition import com.terning.feature.calendar.calendar.component.WeekDaysHeader -import com.terning.feature.calendar.models.CalendarState -import com.terning.feature.calendar.scrap.CalendarScrapListScreen +import com.terning.feature.calendar.calendar.model.CalendarState +import com.terning.feature.calendar.month.CalendarMonthScreen +import com.terning.feature.calendar.scrap.CalendarListScreen +import com.terning.feature.calendar.week.CalendarWeekScreen import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch -import java.time.LocalDate +import java.time.YearMonth @Composable -fun CalendarRoute() { - CalendarScreen() +fun CalendarRoute( + navController: NavController +) { + CalendarScreen( + navController = navController + ) } @Composable -fun CalendarScreen( +private fun CalendarScreen( modifier: Modifier = Modifier, + navController: NavController = rememberNavController(), viewModel: CalendarViewModel = hiltViewModel() ) { - val selectedDate by viewModel.selectedDate.collectAsStateWithLifecycle() + val context = LocalContext.current + val lifecycleOwner = LocalLifecycleOwner.current + val calendarUiState by viewModel.selectedDate.collectAsStateWithLifecycle(lifecycleOwner = lifecycleOwner) + + LaunchedEffect(viewModel.calendarSideEffect, lifecycleOwner) { + viewModel.calendarSideEffect.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle) + .collect { sideEffect -> + when (sideEffect) { + is CalendarSideEffect.ShowToast -> context.toast(sideEffect.message) + } + } + } + val state by remember { mutableStateOf(CalendarState()) } val listState = rememberLazyListState( initialFirstVisibleItemIndex = state.getInitialPage() ) - var currentDate by remember { mutableStateOf(LocalDate.now()) } + var currentDate by remember { mutableStateOf(YearMonth.now()) } var currentPage by remember { mutableIntStateOf(listState.firstVisibleItemIndex) } - var isListExpanded by remember { mutableStateOf(false) } - LaunchedEffect(key1 = listState) { snapshotFlow { listState.firstVisibleItemIndex } .distinctUntilChanged() @@ -76,8 +92,12 @@ fun CalendarScreen( } BackHandler { - if (selectedDate.isEnabled) { - viewModel.updateSelectedDate(selectedDate.selectedDate) + if (calendarUiState.isWeekEnabled) { + viewModel.updateSelectedDate(calendarUiState.selectedDate) + } else if (calendarUiState.isListEnabled) { + viewModel.changeListVisibility() + } else { + navController.navigateUp() } } @@ -87,13 +107,14 @@ fun CalendarScreen( val coroutineScope = rememberCoroutineScope() CalendarTopAppBar( date = currentDate, - isListExpanded = isListExpanded, - isWeekExpanded = selectedDate.isEnabled, + isListExpanded = calendarUiState.isListEnabled, + isWeekExpanded = calendarUiState.isListEnabled, onListButtonClicked = { - isListExpanded = !isListExpanded - if(selectedDate.isEnabled){ + viewModel.changeListVisibility() + if (calendarUiState.isWeekEnabled) { viewModel.disableWeekCalendar() - } }, + } + }, onMonthNavigationButtonClicked = { direction -> coroutineScope.launch { listState.animateScrollToItem( @@ -104,30 +125,13 @@ fun CalendarScreen( ) } ) { 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 { + ScreenTransition( + targetState = !calendarUiState.isListEnabled, + transitionOne = slideInHorizontally { fullWidth -> -fullWidth } togetherWith + slideOutHorizontally { fullWidth -> fullWidth }, + transitionTwo = slideInHorizontally { fullWidth -> fullWidth } togetherWith + slideOutHorizontally { fullWidth -> -fullWidth }, + contentOne = { Column( modifier = Modifier .fillMaxSize() @@ -140,49 +144,44 @@ fun CalendarScreen( 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( + ScreenTransition( + targetState = !calendarUiState.isWeekEnabled, + transitionOne = slideInVertically { fullHeight -> -fullHeight } togetherWith + slideOutVertically { fullHeight -> fullHeight }, + transitionTwo = slideInVertically { fullHeight -> fullHeight } togetherWith + slideOutVertically { fullHeight -> -fullHeight }, + contentOne = { + CalendarMonthScreen( modifier = Modifier.fillMaxSize(), - selectedDate = selectedDate, + calendarUiState = calendarUiState, onDateSelected = { viewModel.updateSelectedDate(it) }, listState = listState, pages = state.getPageCount(), - scrapLists = viewModel.mockScrapList, ) - } else { - CalendarWeekWithScrap( + }, + contentTwo = { + CalendarWeekScreen( modifier = Modifier .fillMaxSize(), - selectedDate = selectedDate, - scrapLists = viewModel.mockScrapList, - onDateSelected = {newDate-> - viewModel.updateSelectedDate(newDate) - } + viewModel = viewModel ) } - } + ) } - } - } - + }, + contentTwo = { + CalendarListScreen( + listState = listState, + pages = state.getPageCount(), + viewModel = viewModel, + modifier = Modifier + .fillMaxSize() + .padding(top = paddingValues.calculateTopPadding()) + ) + }, + ) } } - diff --git a/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarSideEffect.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarSideEffect.kt new file mode 100644 index 000000000..c9026f467 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarSideEffect.kt @@ -0,0 +1,7 @@ +package com.terning.feature.calendar.calendar + +import androidx.annotation.StringRes + +sealed class CalendarSideEffect { + class ShowToast(@StringRes val message: Int) : CalendarSideEffect() +} \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarUiState.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarUiState.kt new file mode 100644 index 000000000..93432ef49 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarUiState.kt @@ -0,0 +1,9 @@ +package com.terning.feature.calendar.calendar + +import java.time.LocalDate + +data class CalendarUiState( + val selectedDate: LocalDate, + val isListEnabled: Boolean = false, + val isWeekEnabled: Boolean = false +) diff --git a/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarViewModel.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarViewModel.kt index 49f263050..b593082b3 100644 --- a/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarViewModel.kt +++ b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarViewModel.kt @@ -2,122 +2,150 @@ package com.terning.feature.calendar.calendar import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.terning.core.designsystem.theme.CalBlue1 -import com.terning.core.designsystem.theme.CalGreen1 -import com.terning.core.designsystem.theme.CalGreen2 -import com.terning.core.designsystem.theme.CalPink -import com.terning.core.designsystem.theme.CalPurple -import com.terning.core.designsystem.theme.CalRed -import com.terning.core.designsystem.theme.CalYellow -import com.terning.feature.calendar.models.Scrap -import com.terning.feature.calendar.models.SelectedDateState +import com.terning.core.state.UiState +import com.terning.domain.repository.CalendarRepository +import com.terning.feature.R +import com.terning.feature.calendar.month.CalendarMonthState +import com.terning.feature.calendar.scrap.CalendarListState +import com.terning.feature.calendar.week.CalendarWeekState import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.time.LocalDate import javax.inject.Inject @HiltViewModel class CalendarViewModel @Inject constructor( + private val calendarRepository: CalendarRepository ) : ViewModel() { - private val _selectedDate = MutableStateFlow( - SelectedDateState( + + private var _selectedDate: MutableStateFlow = MutableStateFlow( + CalendarUiState( selectedDate = LocalDate.now(), - isEnabled = false + isListEnabled = false ) ) + val selectedDate get() = _selectedDate.asStateFlow() + private val _calendarMonthState = MutableStateFlow(CalendarMonthState()) + val calendarMonthState = _calendarMonthState.asStateFlow() + + private val _calendarListState = MutableStateFlow(CalendarListState()) + val calendarListState = _calendarListState.asStateFlow() + + private val _calendarWeekState = MutableStateFlow(CalendarWeekState()) + val calendarWeekState = _calendarWeekState.asStateFlow() + + private val _calendarSideEffect: MutableSharedFlow = MutableSharedFlow() + val calendarSideEffect = _calendarSideEffect.asSharedFlow() + fun updateSelectedDate(date: LocalDate) = viewModelScope.launch { if (_selectedDate.value.selectedDate != date) { _selectedDate.update { currentState -> currentState.copy( selectedDate = date, - isEnabled = true + isWeekEnabled = true ) } + getScrapWeekList() } else { _selectedDate.update { currentState -> currentState.copy( - isEnabled = !currentState.isEnabled + isWeekEnabled = !currentState.isWeekEnabled ) } } } + fun changeListVisibility() { + _selectedDate.update { currentState -> + currentState.copy( + isListEnabled = !currentState.isListEnabled + ) + } + } + fun disableWeekCalendar() { _selectedDate.update { currentState -> currentState.copy( - isEnabled = false + isWeekEnabled = false ) } } + fun getScrapMonth( + year: Int, month: Int + ) = viewModelScope.launch { + withContext(Dispatchers.IO) { + calendarRepository.getScrapMonth(year, month) + }.fold( + onSuccess = { + _calendarMonthState.update { currentState -> + currentState.copy( + loadState = UiState.Success(it) + ) + } + }, + onFailure = { + _calendarSideEffect.emit(CalendarSideEffect.ShowToast(R.string.server_failure)) + } + ) + } + fun getScrapMonthList( + year: Int, month: Int + ) = viewModelScope.launch { + withContext(Dispatchers.IO) { + calendarRepository.getScrapMonthList(year, month) + }.fold( + onSuccess = { + _calendarListState.update { currentState -> + currentState.copy( + loadState = if (it.isNotEmpty()) UiState.Success(it) else UiState.Empty + //loadState = UiState.Success(it) + ) + } + }, + onFailure = { + _calendarListState.update { currentState -> + currentState.copy( + loadState = UiState.Failure(it.message.toString()) + ) - //To be erased in future - val mockScrapList: List> - get() { - val list: MutableList> = mutableListOf() - for (i in 0..30) { - when (i % 6) { - 0 -> { - list.add( - i, - listOf() - ) - } - - 1 -> { - list.add( - i, - listOf( - Scrap("Task1_1", CalBlue1, dDay = "1", period = "3", isScraped = true), - ) - ) - } - - 2 -> { - list.add( - i, - listOf( - Scrap("Task2_1", CalPink, dDay = "2", period = "3", isScraped = true), - Scrap("Task2_2", CalGreen1, dDay = "2", period = "3", isScraped = true) - ) - ) - } - - 3 -> { - list.add( - i, - listOf() - ) - } - - 4 -> { - list.add( - i, - listOf() - ) - } - - 5 -> { - list.add( - i, - listOf( - 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) - ) - ) - } } + _calendarSideEffect.emit(CalendarSideEffect.ShowToast(R.string.server_failure)) } - return list.toList() - } + ) + } + + fun getScrapWeekList() = viewModelScope.launch { + withContext(Dispatchers.IO) { + calendarRepository.getScrapDayList(_selectedDate.value.selectedDate) + }.fold( + onSuccess = { + _calendarWeekState.update { currentState -> + currentState.copy( + loadState = if (it.isNotEmpty()) UiState.Success(it) else UiState.Empty + //loadState = UiState.Success(it) + ) + } + }, + onFailure = { + _calendarWeekState.update { currentState -> + currentState.copy( + loadState = UiState.Failure(it.message.toString()) + ) + + } + _calendarSideEffect.emit(CalendarSideEffect.ShowToast(R.string.server_failure)) + } + ) + } } \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarWeekWithScrap.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarWeekWithScrap.kt deleted file mode 100644 index 54ab943b3..000000000 --- a/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarWeekWithScrap.kt +++ /dev/null @@ -1,80 +0,0 @@ -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.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 -fun CalendarWeekWithScrap( - modifier: Modifier = Modifier, - selectedDate: SelectedDateState, - scrapLists: List> = listOf(), - onDateSelected: (LocalDate) -> Unit -) { - Column( - modifier = modifier - .background(Back) - ) { - Card( - modifier = Modifier - .border( - width = 0.dp, - color = Grey200, - shape = RoundedCornerShape(bottomStart = 20.dp, bottomEnd = 20.dp) - ) - .shadow( - shape = RoundedCornerShape(bottomStart = 20.dp, bottomEnd = 20.dp), - elevation = 1.dp - ), - - shape = RoundedCornerShape(bottomStart = 20.dp, bottomEnd = 20.dp), - ) { - CalendarWeek( - modifier = Modifier - .fillMaxWidth() - .background(White), - selectedDate = selectedDate, - 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 - ) - } - ) - } -} - - - diff --git a/feature/src/main/java/com/terning/feature/calendar/calendar/component/ScreenTransition.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/component/ScreenTransition.kt new file mode 100644 index 000000000..52339d6af --- /dev/null +++ b/feature/src/main/java/com/terning/feature/calendar/calendar/component/ScreenTransition.kt @@ -0,0 +1,37 @@ +package com.terning.feature.calendar.calendar.component + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.ContentTransform +import androidx.compose.animation.SizeTransform +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import com.terning.feature.R + +@Composable +fun ScreenTransition( + targetState: Boolean, + transitionOne: ContentTransform, + transitionTwo: ContentTransform, + contentOne: @Composable () -> Unit, + contentTwo: @Composable () -> Unit +) { + AnimatedContent( + targetState = targetState, + transitionSpec = { + if (targetState) { + transitionOne + } else { + transitionTwo + }.using( + sizeTransform = SizeTransform(clip = true) + ) + }, + label = stringResource(id = R.string.calendar_animation_label) + ) { state -> + if (state) { + contentOne.invoke() + } else { + contentTwo.invoke() + } + } +} \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/calendar/calendar/component/WeekDaysHeader.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/component/WeekDaysHeader.kt index e02bde2ce..554a3cd1d 100644 --- a/feature/src/main/java/com/terning/feature/calendar/calendar/component/WeekDaysHeader.kt +++ b/feature/src/main/java/com/terning/feature/calendar/calendar/component/WeekDaysHeader.kt @@ -22,39 +22,40 @@ import com.terning.feature.R fun WeekDaysHeader( modifier: Modifier = Modifier, ) { - Row( - modifier = modifier - .fillMaxWidth() - .background(Color.White) - .padding( - horizontal = 20.dp, - vertical = 18.dp - ), - ){ - val dayOfWeek = listOf( - R.string.calendar_text_sunday, - R.string.calendar_text_monday, - R.string.calendar_text_tuesday, - R.string.calendar_text_wednesday, - R.string.calendar_text_thursday, - R.string.calendar_text_friday, - R.string.calendar_text_saturday,) - dayOfWeek.forEach { day -> - Text( - modifier = Modifier.weight(1f), - text = stringResource(id = day), - style = TerningTheme.typography.body7, - color = if(day == R.string.calendar_text_sunday) SundayRed else Black, - textAlign = TextAlign.Center - ) - } + Row( + modifier = modifier + .fillMaxWidth() + .background(Color.White) + .padding( + horizontal = 20.dp, + vertical = 18.dp + ), + ) { + val dayOfWeek = listOf( + R.string.calendar_text_sunday, + R.string.calendar_text_monday, + R.string.calendar_text_tuesday, + R.string.calendar_text_wednesday, + R.string.calendar_text_thursday, + R.string.calendar_text_friday, + R.string.calendar_text_saturday, + ) + dayOfWeek.forEach { day -> + Text( + modifier = Modifier.weight(1f), + text = stringResource(id = day), + style = TerningTheme.typography.body7, + color = if (day == R.string.calendar_text_sunday) SundayRed else Black, + textAlign = TextAlign.Center + ) } + } } @Preview(showBackground = true) @Composable fun WeekDaysHeaderPreview() { - TerningPointTheme{ + TerningPointTheme { WeekDaysHeader() } } \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/calendar/models/CalendarDefault.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/model/CalendarDefault.kt similarity index 96% rename from feature/src/main/java/com/terning/feature/calendar/models/CalendarDefault.kt rename to feature/src/main/java/com/terning/feature/calendar/calendar/model/CalendarDefault.kt index 59a554d7d..1d2cf981a 100644 --- a/feature/src/main/java/com/terning/feature/calendar/models/CalendarDefault.kt +++ b/feature/src/main/java/com/terning/feature/calendar/calendar/model/CalendarDefault.kt @@ -1,4 +1,4 @@ -package com.terning.feature.calendar.models +package com.terning.feature.calendar.calendar.model import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.gestures.FlingBehavior diff --git a/feature/src/main/java/com/terning/feature/calendar/models/CalendarState.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/model/CalendarState.kt similarity index 96% rename from feature/src/main/java/com/terning/feature/calendar/models/CalendarState.kt rename to feature/src/main/java/com/terning/feature/calendar/calendar/model/CalendarState.kt index 98e3cfd94..90c00f759 100644 --- a/feature/src/main/java/com/terning/feature/calendar/models/CalendarState.kt +++ b/feature/src/main/java/com/terning/feature/calendar/calendar/model/CalendarState.kt @@ -1,4 +1,4 @@ -package com.terning.feature.calendar.models +package com.terning.feature.calendar.calendar.model import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue diff --git a/feature/src/main/java/com/terning/feature/calendar/navigation/CalendarNavigation.kt b/feature/src/main/java/com/terning/feature/calendar/calendar/navigation/CalendarNavigation.kt similarity index 72% rename from feature/src/main/java/com/terning/feature/calendar/navigation/CalendarNavigation.kt rename to feature/src/main/java/com/terning/feature/calendar/calendar/navigation/CalendarNavigation.kt index 1725d9eb7..83037a00c 100644 --- a/feature/src/main/java/com/terning/feature/calendar/navigation/CalendarNavigation.kt +++ b/feature/src/main/java/com/terning/feature/calendar/calendar/navigation/CalendarNavigation.kt @@ -1,4 +1,4 @@ -package com.terning.feature.calendar.navigation +package com.terning.feature.calendar.calendar.navigation import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder @@ -16,9 +16,13 @@ fun NavController.navigateCalendar(navOptions: NavOptions? = null) { ) } -fun NavGraphBuilder.calendarNavGraph() { +fun NavGraphBuilder.calendarNavGraph( + navHostController: NavController +) { composable { - CalendarRoute() + CalendarRoute( + navController = navHostController + ) } } diff --git a/feature/src/main/java/com/terning/feature/calendar/models/SelectedDateState.kt b/feature/src/main/java/com/terning/feature/calendar/models/SelectedDateState.kt deleted file mode 100644 index 04864c848..000000000 --- a/feature/src/main/java/com/terning/feature/calendar/models/SelectedDateState.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.terning.feature.calendar.models - -import java.time.LocalDate - -data class SelectedDateState( - val selectedDate: LocalDate, - val isEnabled: Boolean -) diff --git a/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarMonth.kt b/feature/src/main/java/com/terning/feature/calendar/month/CalendarMonth.kt similarity index 66% rename from feature/src/main/java/com/terning/feature/calendar/calendar/CalendarMonth.kt rename to feature/src/main/java/com/terning/feature/calendar/month/CalendarMonth.kt index 3d1822b5c..e69cecce0 100644 --- a/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarMonth.kt +++ b/feature/src/main/java/com/terning/feature/calendar/month/CalendarMonth.kt @@ -1,12 +1,8 @@ -package com.terning.feature.calendar.calendar +package com.terning.feature.calendar.month -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -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.HorizontalDivider import androidx.compose.runtime.Composable @@ -16,11 +12,13 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.terning.core.designsystem.theme.Grey150 import com.terning.core.designsystem.theme.TerningPointTheme +import com.terning.core.extension.getDateAsMapString 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 com.terning.domain.entity.response.CalendarScrapModel +import com.terning.feature.calendar.calendar.CalendarUiState +import com.terning.feature.calendar.month.component.CalendarDay +import com.terning.feature.calendar.month.model.MonthData +import com.terning.feature.calendar.scrap.CalendarMonthScrap import java.time.LocalDate import java.time.YearMonth @@ -29,8 +27,8 @@ fun CalendarMonth( modifier: Modifier = Modifier, monthData: MonthData, onDateSelected: (LocalDate) -> Unit, - selectedDate: SelectedDateState, - scrapLists: List> = listOf() + calendarUiState: CalendarUiState, + scrapMap: Map> = mapOf() ) { Column( modifier = modifier @@ -51,20 +49,20 @@ fun CalendarMonth( ) { CalendarDay( dayData = day, - isSelected = selectedDate.selectedDate == day.date && selectedDate.isEnabled, + isSelected = calendarUiState.selectedDate == day.date && calendarUiState.isWeekEnabled, isToday = day.date.isToday(), onDateSelected = onDateSelected ) - if(!day.isOutDate) { - val index = day.date.dayOfMonth - 1 - CalendarScrapStrip( - scrapList = scrapLists[index] + if (!day.isOutDate) { + val index = day.date.getDateAsMapString() + CalendarMonthScrap( + scrapLists = scrapMap[index].orEmpty() ) } } } } - if(month.indexOf(week) != month.lastIndex) { + if (month.indexOf(week) != month.lastIndex) { HorizontalDivider( thickness = 1.dp, color = Grey150 @@ -80,9 +78,8 @@ fun CalendarMonthPreview() { TerningPointTheme { CalendarMonth( monthData = MonthData(YearMonth.now()), - selectedDate = SelectedDateState(LocalDate.now(), true), + calendarUiState = CalendarUiState(LocalDate.now(), true), onDateSelected = {}, - scrapLists = listOf() ) } } \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/calendar/month/CalendarMonthScreen.kt b/feature/src/main/java/com/terning/feature/calendar/month/CalendarMonthScreen.kt new file mode 100644 index 000000000..402910a1e --- /dev/null +++ b/feature/src/main/java/com/terning/feature/calendar/month/CalendarMonthScreen.kt @@ -0,0 +1,98 @@ +package com.terning.feature.calendar.month + +import androidx.compose.foundation.background +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.snapshotFlow +import androidx.compose.ui.Modifier +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.terning.core.designsystem.theme.White +import com.terning.core.state.UiState +import com.terning.domain.entity.response.CalendarScrapModel +import com.terning.feature.calendar.calendar.CalendarUiState +import com.terning.feature.calendar.calendar.CalendarViewModel +import com.terning.feature.calendar.calendar.model.CalendarDefaults.flingBehavior +import com.terning.feature.calendar.calendar.model.CalendarState.Companion.getDateByPage +import com.terning.feature.calendar.month.model.MonthData +import kotlinx.coroutines.flow.distinctUntilChanged +import java.time.LocalDate +import java.time.YearMonth + +@Composable +fun CalendarMonthScreen( + pages: Int, + listState: LazyListState, + calendarUiState: CalendarUiState, + onDateSelected: (LocalDate) -> Unit, + modifier: Modifier = Modifier, + viewModel: CalendarViewModel = hiltViewModel() +) { + val lifecycleOwner = LocalLifecycleOwner.current + val scrapState by viewModel.calendarMonthState.collectAsStateWithLifecycle(lifecycleOwner) + + LaunchedEffect(key1 = listState) { + snapshotFlow { listState.firstVisibleItemIndex } + .distinctUntilChanged() + .collect { + val page = listState.firstVisibleItemIndex + val date = getDateByPage(page) + viewModel.getScrapMonth(date.year, date.monthValue) + } + } + + when (scrapState.loadState) { + UiState.Loading -> {} + UiState.Empty -> {} + is UiState.Failure -> {} + is UiState.Success -> { + val scrapMap = (scrapState.loadState as UiState.Success).data + HorizontalCalendar( + pages = pages, + listState = listState, + calendarUiState = calendarUiState, + scrapMap = scrapMap, + onDateSelected = onDateSelected, + modifier = modifier + ) + } + } +} + +@Composable +fun HorizontalCalendar( + pages: Int, + listState: LazyListState, + calendarUiState: CalendarUiState, + scrapMap: Map>, + onDateSelected: (LocalDate) -> Unit, + modifier: Modifier = Modifier, +) { + LazyRow( + modifier = modifier + .background(White), + state = listState, + userScrollEnabled = true, + flingBehavior = flingBehavior( + state = listState + ) + ) { + items(pages) { page -> + val date = getDateByPage(page) + val monthData = MonthData(YearMonth.of(date.year, date.month)) + + CalendarMonth( + modifier = Modifier.fillParentMaxSize(), + calendarUiState = calendarUiState, + onDateSelected = onDateSelected, + monthData = monthData, + scrapMap = scrapMap + ) + } + } +} diff --git a/feature/src/main/java/com/terning/feature/calendar/month/CalendarMonthState.kt b/feature/src/main/java/com/terning/feature/calendar/month/CalendarMonthState.kt new file mode 100644 index 000000000..4e37e7b1f --- /dev/null +++ b/feature/src/main/java/com/terning/feature/calendar/month/CalendarMonthState.kt @@ -0,0 +1,8 @@ +package com.terning.feature.calendar.month + +import com.terning.core.state.UiState +import com.terning.domain.entity.response.CalendarScrapModel + +data class CalendarMonthState( + val loadState: UiState>> = UiState.Loading +) \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarDay.kt b/feature/src/main/java/com/terning/feature/calendar/month/component/CalendarDay.kt similarity index 90% rename from feature/src/main/java/com/terning/feature/calendar/calendar/CalendarDay.kt rename to feature/src/main/java/com/terning/feature/calendar/month/component/CalendarDay.kt index 14a3b8656..bb7788ea6 100644 --- a/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarDay.kt +++ b/feature/src/main/java/com/terning/feature/calendar/month/component/CalendarDay.kt @@ -1,4 +1,4 @@ -package com.terning.feature.calendar.calendar +package com.terning.feature.calendar.month.component import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -22,13 +22,13 @@ 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.designsystem.theme.White -import com.terning.feature.calendar.models.DayModel +import com.terning.feature.calendar.month.model.DayClass import java.time.LocalDate @Composable fun CalendarDay( modifier: Modifier = Modifier, - dayData: DayModel, + dayData: DayClass, isSelected: Boolean, isToday: Boolean, onDateSelected: (LocalDate) -> Unit @@ -82,19 +82,19 @@ fun CalendarDayPreview() { TerningPointTheme { Row { CalendarDay( - dayData = DayModel(LocalDate.now(), false), + dayData = DayClass(LocalDate.now(), false), isSelected = true, isToday = true, onDateSelected = {} ) CalendarDay( - dayData = DayModel(LocalDate.now(), false), + dayData = DayClass(LocalDate.now(), false), isSelected = false, isToday = true, onDateSelected = {} ) CalendarDay( - dayData = DayModel(LocalDate.now(), false), + dayData = DayClass(LocalDate.now(), false), isSelected = false, isToday = false, onDateSelected = {} diff --git a/feature/src/main/java/com/terning/feature/calendar/models/DayModel.kt b/feature/src/main/java/com/terning/feature/calendar/month/model/DayClass.kt similarity index 67% rename from feature/src/main/java/com/terning/feature/calendar/models/DayModel.kt rename to feature/src/main/java/com/terning/feature/calendar/month/model/DayClass.kt index 891a9f470..814175be4 100644 --- a/feature/src/main/java/com/terning/feature/calendar/models/DayModel.kt +++ b/feature/src/main/java/com/terning/feature/calendar/month/model/DayClass.kt @@ -1,10 +1,10 @@ -package com.terning.feature.calendar.models +package com.terning.feature.calendar.month.model import androidx.compose.runtime.Immutable import java.time.LocalDate @Immutable -data class DayModel( +data class DayClass( val date: LocalDate, val isOutDate: Boolean = false ) \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/calendar/models/MonthData.kt b/feature/src/main/java/com/terning/feature/calendar/month/model/MonthData.kt similarity index 82% rename from feature/src/main/java/com/terning/feature/calendar/models/MonthData.kt rename to feature/src/main/java/com/terning/feature/calendar/month/model/MonthData.kt index 425f7beb6..365720da5 100644 --- a/feature/src/main/java/com/terning/feature/calendar/models/MonthData.kt +++ b/feature/src/main/java/com/terning/feature/calendar/month/model/MonthData.kt @@ -1,4 +1,4 @@ -package com.terning.feature.calendar.models +package com.terning.feature.calendar.month.model import androidx.compose.runtime.Immutable import java.time.YearMonth @@ -10,7 +10,7 @@ import java.time.YearMonth * [inDays] represents the number of days in the previous month that should be shown before the first day of the month. * [outDays] represents the number of days in the next month that should be shown after the last day of the month.] * [totalDays] represents the total number of days shown on calendar - * [calendarMonth] represents the list of days of the month, a list of [DayModel] + * [calendarMonth] represents the list of days of the month, a list of [DayClass] */ @@ -29,13 +29,14 @@ data class MonthData( private val rows = (0 until totalDays).chunked(7) - val calendarMonth = MonthModel(month, rows.map { week -> week.map {dayOffset -> getDay(dayOffset)}}) + val calendarMonth = + MonthModel(month, rows.map { week -> week.map { dayOffset -> getDay(dayOffset) } }) - private fun getDay(dayOffset: Int): DayModel { + private fun getDay(dayOffset: Int): DayClass { val firstDayOnCalendar = month.atDay(1).minusDays(inDays.toLong()) val date = firstDayOnCalendar.plusDays(dayOffset.toLong()) val isOutDate = YearMonth.of(date.year, date.monthValue) != month - return DayModel(date, isOutDate) + return DayClass(date, isOutDate) } } diff --git a/feature/src/main/java/com/terning/feature/calendar/models/MonthModel.kt b/feature/src/main/java/com/terning/feature/calendar/month/model/MonthModel.kt similarity index 92% rename from feature/src/main/java/com/terning/feature/calendar/models/MonthModel.kt rename to feature/src/main/java/com/terning/feature/calendar/month/model/MonthModel.kt index 01a10041d..389df4894 100644 --- a/feature/src/main/java/com/terning/feature/calendar/models/MonthModel.kt +++ b/feature/src/main/java/com/terning/feature/calendar/month/model/MonthModel.kt @@ -1,4 +1,4 @@ -package com.terning.feature.calendar.models +package com.terning.feature.calendar.month.model import androidx.compose.runtime.Immutable import java.time.YearMonth @@ -6,7 +6,7 @@ import java.time.YearMonth @Immutable data class MonthModel( val yearMonth: YearMonth, - val weekDays: List> + val weekDays: List> ) { override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarListScreen.kt b/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarListScreen.kt new file mode 100644 index 000000000..04bdf8532 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarListScreen.kt @@ -0,0 +1,124 @@ +package com.terning.feature.calendar.scrap + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.snapshotFlow +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +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.designsystem.theme.Back +import com.terning.core.designsystem.theme.Grey400 +import com.terning.core.designsystem.theme.TerningTheme +import com.terning.core.designsystem.theme.White +import com.terning.core.extension.getDateAsMapString +import com.terning.core.extension.isListNotEmpty +import com.terning.core.state.UiState +import com.terning.feature.R +import com.terning.feature.calendar.calendar.CalendarViewModel +import com.terning.feature.calendar.calendar.model.CalendarDefaults.flingBehavior +import com.terning.feature.calendar.calendar.model.CalendarState.Companion.getDateByPage +import com.terning.feature.calendar.scrap.component.CalendarScrapList +import kotlinx.coroutines.flow.distinctUntilChanged +import java.time.LocalDate + +@Composable +fun CalendarListScreen( + pages: Int, + listState: LazyListState, + modifier: Modifier = Modifier, + viewModel: CalendarViewModel = hiltViewModel() +) { + val lifecycleOwner = LocalLifecycleOwner.current + val scrapState by viewModel.calendarListState.collectAsStateWithLifecycle(lifecycleOwner) + + LaunchedEffect(key1 = listState) { + snapshotFlow { listState.firstVisibleItemIndex } + .distinctUntilChanged() + .collect { + val page = listState.firstVisibleItemIndex + val date = getDateByPage(page) + viewModel.getScrapMonthList(date.year, date.monthValue) + } + } + + + 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) + ) { + when (scrapState.loadState) { + UiState.Loading -> {} + UiState.Empty -> { + item { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + 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 + ) + } + } + } + + is UiState.Failure -> {} + is UiState.Success -> { + items(getDate.lengthOfMonth()) { day -> + val scrapMap = (scrapState.loadState as UiState.Success).data + val currentDate = + LocalDate.of(getDate.year, getDate.monthValue, day + 1) + val dateIndex = currentDate.getDateAsMapString() + + if (scrapMap[dateIndex].isListNotEmpty()) { + CalendarScrapList( + selectedDate = currentDate, + scrapList = scrapMap[dateIndex].orEmpty(), + isFromList = true, + + noScrapScreen = {}) + } + } + } + } + } + } + } +} + + diff --git a/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarListState.kt b/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarListState.kt new file mode 100644 index 000000000..10fa93cdb --- /dev/null +++ b/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarListState.kt @@ -0,0 +1,8 @@ +package com.terning.feature.calendar.scrap + +import com.terning.core.state.UiState +import com.terning.domain.entity.response.CalendarScrapDetailModel + +data class CalendarListState( + val loadState: UiState>> = UiState.Loading +) \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrapStrip.kt b/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarMonthScrap.kt similarity index 61% rename from feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrapStrip.kt rename to feature/src/main/java/com/terning/feature/calendar/scrap/CalendarMonthScrap.kt index 34e8bdcf3..089a9f654 100644 --- a/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrapStrip.kt +++ b/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarMonthScrap.kt @@ -5,42 +5,49 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.terning.core.designsystem.theme.TerningTheme import com.terning.core.designsystem.theme.White -import com.terning.feature.calendar.models.Scrap +import com.terning.domain.entity.response.CalendarScrapModel @Composable -fun CalendarScrapStrip( +fun CalendarMonthScrap( modifier: Modifier = Modifier, - scrapList: List + scrapLists: List ) { LazyColumn( - modifier = modifier.fillMaxWidth().padding(top = 3.dp) + modifier = modifier.fillMaxWidth() ) { - items(scrapList.subList(0, MAX_SCRAP_COUNT.coerceAtMost(scrapList.size))) { scrap -> + items(scrapLists.subList(0, MAX_SCRAP_COUNT.coerceAtMost(scrapLists.size))) { scrap -> Text( - text = scrap.text, + text = scrap.title, style = TerningTheme.typography.body5, textAlign = TextAlign.Center, color = White, maxLines = 1, overflow = TextOverflow.Ellipsis, modifier = Modifier + .padding(top = 2.dp) + .padding(horizontal = 2.dp) .fillMaxWidth() - .background(color = scrap.backgroundColor) + .background( + color = Color(android.graphics.Color.parseColor(scrap.color)), + shape = RoundedCornerShape(size = 1.dp) + ) ) } - item{ - if(scrapList.size > MAX_SCRAP_COUNT) { + item { + if (scrapLists.size > MAX_SCRAP_COUNT) { Text( - text = "+${(scrapList.size - MAX_SCRAP_COUNT)}", + text = "+${(scrapLists.size - MAX_SCRAP_COUNT)}", style = TerningTheme.typography.detail4, textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth() 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 deleted file mode 100644 index b4b915107..000000000 --- a/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrapListScreen.kt +++ /dev/null @@ -1,73 +0,0 @@ -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 -> - runCatching { - LocalDate.of(getDate.year, getDate.monthValue, day + 1) - }.onSuccess { - CalendarScrapList( - selectedDate = it, - 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/scrap/CalendarScrap.kt b/feature/src/main/java/com/terning/feature/calendar/scrap/component/CalendarScrap.kt similarity index 52% rename from feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrap.kt rename to feature/src/main/java/com/terning/feature/calendar/scrap/component/CalendarScrap.kt index e7d7c5a3f..239618a87 100644 --- a/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrap.kt +++ b/feature/src/main/java/com/terning/feature/calendar/scrap/component/CalendarScrap.kt @@ -1,29 +1,30 @@ -package com.terning.feature.calendar.scrap +package com.terning.feature.calendar.scrap.component import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color 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.feature.calendar.models.Scrap +import com.terning.domain.entity.response.CalendarScrapDetailModel @Composable fun CalendarScrap( - scrap: Scrap, + scrap: CalendarScrapDetailModel, modifier: Modifier = Modifier, ) { ScrapBox( modifier = modifier, cornerRadius = 10.dp, - scrapColor = scrap.backgroundColor, + scrapColor = Color(android.graphics.Color.parseColor(scrap.color)), elevation = 1.dp, ) { InternItem( - imageUrl = scrap.image.orEmpty(), - title = scrap.text, + imageUrl = scrap.companyImage.orEmpty(), + title = scrap.title, dateDeadline = scrap.dDay, - workingPeriod = scrap.period, - isScraped = scrap.isScraped + workingPeriod = scrap.workingPeriod, + isScraped = scrap.isScrapped ) } } \ 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/component/CalendarScrapList.kt similarity index 83% rename from feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrapList.kt rename to feature/src/main/java/com/terning/feature/calendar/scrap/component/CalendarScrapList.kt index d1a9dc1ff..844e4e523 100644 --- a/feature/src/main/java/com/terning/feature/calendar/scrap/CalendarScrapList.kt +++ b/feature/src/main/java/com/terning/feature/calendar/scrap/component/CalendarScrapList.kt @@ -1,4 +1,4 @@ -package com.terning.feature.calendar.scrap +package com.terning.feature.calendar.scrap.component import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -14,18 +14,20 @@ 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 com.terning.core.extension.isListNotEmpty +import com.terning.domain.entity.response.CalendarScrapDetailModel import java.time.LocalDate @Composable fun CalendarScrapList( selectedDate: LocalDate, - scrapLists: List>, + scrapList: List, isFromList: Boolean = false, noScrapScreen: @Composable () -> Unit ) { val scrollState = rememberScrollState() - if (scrapLists[selectedDate.dayOfMonth - 1].isNotEmpty()) { + + if (scrapList.isListNotEmpty()) { Text( text = selectedDate.getDateStringInKorean(), style = TerningTheme.typography.title5, @@ -44,13 +46,13 @@ fun CalendarScrapList( .padding(horizontal = 24.dp) } - if (scrapLists[selectedDate.dayOfMonth - 1].isEmpty()) { + if (!scrapList.isListNotEmpty()) { noScrapScreen() } else { Column( modifier = topModifier ) { - for (scrap in scrapLists[selectedDate.dayOfMonth - 1]) { + for (scrap in scrapList) { CalendarScrap( scrap = scrap ) diff --git a/feature/src/main/java/com/terning/feature/calendar/models/Scrap.kt b/feature/src/main/java/com/terning/feature/calendar/scrap/model/Scrap.kt similarity index 85% rename from feature/src/main/java/com/terning/feature/calendar/models/Scrap.kt rename to feature/src/main/java/com/terning/feature/calendar/scrap/model/Scrap.kt index 86d00350e..f7cc07035 100644 --- a/feature/src/main/java/com/terning/feature/calendar/models/Scrap.kt +++ b/feature/src/main/java/com/terning/feature/calendar/scrap/model/Scrap.kt @@ -1,4 +1,4 @@ -package com.terning.feature.calendar.models +package com.terning.feature.calendar.scrap.model import androidx.compose.runtime.Immutable import androidx.compose.ui.graphics.Color diff --git a/feature/src/main/java/com/terning/feature/calendar/week/CalendarWeekScreen.kt b/feature/src/main/java/com/terning/feature/calendar/week/CalendarWeekScreen.kt new file mode 100644 index 000000000..817bcdd17 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/calendar/week/CalendarWeekScreen.kt @@ -0,0 +1,125 @@ +package com.terning.feature.calendar.week + +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.fillMaxWidth +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.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +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 androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.compose.collectAsStateWithLifecycle +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.core.state.UiState +import com.terning.domain.entity.response.CalendarScrapDetailModel +import com.terning.feature.R +import com.terning.feature.calendar.calendar.CalendarViewModel +import com.terning.feature.calendar.scrap.component.CalendarScrapList +import java.time.LocalDate + +@Composable +fun CalendarWeekScreen( + modifier: Modifier = Modifier, + viewModel: CalendarViewModel = hiltViewModel() +) { + val lifecycleOwner = LocalLifecycleOwner.current + val uiState by viewModel.selectedDate.collectAsStateWithLifecycle(lifecycleOwner) + val calendarWeekState by viewModel.calendarWeekState.collectAsStateWithLifecycle(lifecycleOwner) + + LaunchedEffect(uiState.selectedDate) { + viewModel.getScrapWeekList() + } + + Column( + modifier = modifier + .background(Back) + ) { + Card( + modifier = Modifier + .border( + width = 0.dp, + color = Grey200, + shape = RoundedCornerShape(bottomStart = 20.dp, bottomEnd = 20.dp) + ) + .shadow( + shape = RoundedCornerShape(bottomStart = 20.dp, bottomEnd = 20.dp), + elevation = 1.dp + ), + + shape = RoundedCornerShape(bottomStart = 20.dp, bottomEnd = 20.dp), + ) { + HorizontalCalendarWeek( + modifier = Modifier + .fillMaxWidth() + .background(White), + selectedDate = uiState, + onDateSelected = { + viewModel.updateSelectedDate(it) + } + ) + } + + when (calendarWeekState.loadState) { + is UiState.Loading -> {} + is UiState.Empty -> { + CalendarWeekEmpty() + } + + is UiState.Failure -> {} + is UiState.Success -> { + val scrapList = (calendarWeekState.loadState as UiState.Success).data + CalendarWeekSuccess(scrapList = scrapList, selectedDate = uiState.selectedDate) + } + } + } +} + +@Composable +fun CalendarWeekEmpty( + modifier: Modifier = Modifier +) { + Box( + contentAlignment = Alignment.Center + ) { + + + 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 + ) + } +} + +@Composable +fun CalendarWeekSuccess( + scrapList: List, + selectedDate: LocalDate, +) { + + CalendarScrapList(selectedDate = selectedDate, scrapList = scrapList) {} +} + + + + diff --git a/feature/src/main/java/com/terning/feature/calendar/week/CalendarWeekState.kt b/feature/src/main/java/com/terning/feature/calendar/week/CalendarWeekState.kt new file mode 100644 index 000000000..b94f2a54f --- /dev/null +++ b/feature/src/main/java/com/terning/feature/calendar/week/CalendarWeekState.kt @@ -0,0 +1,8 @@ +package com.terning.feature.calendar.week + +import com.terning.core.state.UiState +import com.terning.domain.entity.response.CalendarScrapDetailModel + +data class CalendarWeekState( + val loadState: UiState> = UiState.Loading +) \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarWeek.kt b/feature/src/main/java/com/terning/feature/calendar/week/HorizontalCalendarWeek.kt similarity index 77% rename from feature/src/main/java/com/terning/feature/calendar/calendar/CalendarWeek.kt rename to feature/src/main/java/com/terning/feature/calendar/week/HorizontalCalendarWeek.kt index ea6bb76b6..9b16f9a9d 100644 --- a/feature/src/main/java/com/terning/feature/calendar/calendar/CalendarWeek.kt +++ b/feature/src/main/java/com/terning/feature/calendar/week/HorizontalCalendarWeek.kt @@ -1,4 +1,4 @@ -package com.terning.feature.calendar.calendar +package com.terning.feature.calendar.week import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.fillMaxWidth @@ -12,16 +12,15 @@ import androidx.compose.ui.Modifier 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 com.terning.feature.calendar.calendar.CalendarUiState +import com.terning.feature.calendar.month.component.CalendarDay +import com.terning.feature.calendar.month.model.MonthData import java.time.LocalDate import java.time.YearMonth @Composable -fun CalendarWeek( - selectedDate: SelectedDateState, +fun HorizontalCalendarWeek( + selectedDate: CalendarUiState, modifier: Modifier = Modifier, onDateSelected: (LocalDate) -> Unit = {} ) { @@ -29,14 +28,15 @@ fun CalendarWeek( val monthData = MonthData(YearMonth.of(date.year, date.monthValue)) val currentWeek = date.getWeekIndexContainingSelectedDate(monthData.inDays) - val pagerState = rememberPagerState ( + val pagerState = rememberPagerState( initialPage = currentWeek, - pageCount = {monthData.totalDays / 7} + pageCount = { monthData.totalDays / 7 } ) HorizontalPager( modifier = modifier, - state = pagerState) { page -> + state = pagerState + ) { page -> LazyRow( modifier = Modifier .fillMaxWidth() @@ -46,7 +46,7 @@ fun CalendarWeek( items(items = monthData.calendarMonth.weekDays[page]) { day -> CalendarDay( dayData = day, - isSelected = selectedDate.selectedDate == day.date && selectedDate.isEnabled, + isSelected = selectedDate.selectedDate == day.date && selectedDate.isWeekEnabled, isToday = day.date.isToday(), onDateSelected = onDateSelected ) diff --git a/feature/src/main/java/com/terning/feature/main/MainNavigator.kt b/feature/src/main/java/com/terning/feature/main/MainNavigator.kt index b538c3e58..736728c99 100644 --- a/feature/src/main/java/com/terning/feature/main/MainNavigator.kt +++ b/feature/src/main/java/com/terning/feature/main/MainNavigator.kt @@ -9,7 +9,7 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import androidx.navigation.navOptions -import com.terning.feature.calendar.navigation.navigateCalendar +import com.terning.feature.calendar.calendar.navigation.navigateCalendar import com.terning.feature.home.home.navigation.navigateHome import com.terning.feature.mypage.navigation.navigateMyPage import com.terning.feature.onboarding.signin.navigation.SignIn diff --git a/feature/src/main/java/com/terning/feature/main/MainScreen.kt b/feature/src/main/java/com/terning/feature/main/MainScreen.kt index e97b15b58..b5f8e5d1c 100644 --- a/feature/src/main/java/com/terning/feature/main/MainScreen.kt +++ b/feature/src/main/java/com/terning/feature/main/MainScreen.kt @@ -20,7 +20,7 @@ import com.terning.core.designsystem.theme.Grey300 import com.terning.core.designsystem.theme.TerningMain import com.terning.core.designsystem.theme.White import com.terning.core.util.NoRippleInteractionSource -import com.terning.feature.calendar.navigation.calendarNavGraph +import com.terning.feature.calendar.calendar.navigation.calendarNavGraph import com.terning.feature.home.changefilter.navigation.changeFilterNavGraph import com.terning.feature.home.home.navigation.homeNavGraph import com.terning.feature.intern.navigation.internNavGraph @@ -59,7 +59,7 @@ fun MainScreen( startDestination = navigator.startDestination ) { homeNavGraph(navHostController = navigator.navController) - calendarNavGraph() + calendarNavGraph(navHostController = navigator.navController) searchNavGraph(navHostController = navigator.navController) myPageNavGraph() signInNavGraph(navHostController = navigator.navController) diff --git a/feature/src/main/java/com/terning/feature/main/MainTab.kt b/feature/src/main/java/com/terning/feature/main/MainTab.kt index 3d35d1ac4..179669c64 100644 --- a/feature/src/main/java/com/terning/feature/main/MainTab.kt +++ b/feature/src/main/java/com/terning/feature/main/MainTab.kt @@ -6,7 +6,7 @@ import androidx.compose.runtime.Composable import com.terning.core.navigation.MainTabRoute import com.terning.core.navigation.Route import com.terning.feature.R -import com.terning.feature.calendar.navigation.Calendar +import com.terning.feature.calendar.calendar.navigation.Calendar import com.terning.feature.home.home.navigation.Home import com.terning.feature.mypage.navigation.MyPage import com.terning.feature.search.search.navigation.Search