diff --git a/.kotlin/sessions/kotlin-compiler-9280088279672338011.salive b/.kotlin/sessions/kotlin-compiler-9280088279672338011.salive new file mode 100644 index 00000000..e69de29b diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index db44d97f..6727d01a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - Dplay + 디플레이 디플레이 오늘의 질문이 도착했어요 \ No newline at end of file diff --git a/core/data/src/main/java/com/example/data/datasource/remote/AuthRemoteDataSource.kt b/core/data/src/main/java/com/example/data/datasource/remote/AuthRemoteDataSource.kt index dc18566a..c6f3c46c 100644 --- a/core/data/src/main/java/com/example/data/datasource/remote/AuthRemoteDataSource.kt +++ b/core/data/src/main/java/com/example/data/datasource/remote/AuthRemoteDataSource.kt @@ -126,7 +126,7 @@ class AuthRemoteDataSource suspend fun reissue(refreshToken: String): TokenResponse { try { - val response = authService.reissue(refreshToken = refreshToken) + val response = authService.reissue(refreshToken = "Bearer $refreshToken") return response.data ?: throw Exception("Data is null") } catch (e: Exception) { throw e diff --git a/core/data/src/main/java/com/example/data/datasource/remote/QuestionPostsPagingSource.kt b/core/data/src/main/java/com/example/data/datasource/remote/QuestionPostsPagingSource.kt index 428ef20a..69b95655 100644 --- a/core/data/src/main/java/com/example/data/datasource/remote/QuestionPostsPagingSource.kt +++ b/core/data/src/main/java/com/example/data/datasource/remote/QuestionPostsPagingSource.kt @@ -9,6 +9,7 @@ class QuestionPostsPagingSource( private val postService: PostService, private val questionId: Long, private val onTotalCountFetched: (Int) -> Unit, + private val onLockedFetched: (Boolean) -> Unit, ) : PagingSource() { override fun getRefreshKey(state: PagingState): String? = null @@ -26,6 +27,7 @@ class QuestionPostsPagingSource( val data = response.data ?: throw Exception("data is null") if (params.key == null) { onTotalCountFetched(data.totalCount) + onLockedFetched(data.locked) } val posts = data.items val nextCursor = data.nextCursor diff --git a/core/data/src/main/java/com/example/data/mapper/todomain/TodayPosteResponseMapper.kt b/core/data/src/main/java/com/example/data/mapper/todomain/TodayPosteResponseMapper.kt index 19759aa9..2d196ffc 100644 --- a/core/data/src/main/java/com/example/data/mapper/todomain/TodayPosteResponseMapper.kt +++ b/core/data/src/main/java/com/example/data/mapper/todomain/TodayPosteResponseMapper.kt @@ -3,7 +3,7 @@ package com.example.data.mapper.todomain import com.example.data.model.response.TodayPostItemResponse import com.example.data.model.response.TodayPostTrackResponse import com.example.data.model.response.TodayPostsResponse -import com.example.domain.model.BADGE +import com.example.domain.model.Badge import com.example.domain.model.DailyQuestion import com.example.domain.model.FeedItem import com.example.domain.model.HomeScreenData @@ -28,7 +28,7 @@ fun TodayPostItemResponse.toDomain(): FeedItem = postId = postId, isScrapped = isScrapped, content = content, - badge = badge?.let { BADGE.valueOf(it) }, + badge = badge?.let { Badge.valueOf(it) }, track = track.toDomain(), writer = user.toDomain(), like = like.toDomain(), diff --git a/core/data/src/main/java/com/example/data/mapper/todomain/UserResponseMapper.kt b/core/data/src/main/java/com/example/data/mapper/todomain/UserResponseMapper.kt index 85c7c5b5..00eba51a 100644 --- a/core/data/src/main/java/com/example/data/mapper/todomain/UserResponseMapper.kt +++ b/core/data/src/main/java/com/example/data/mapper/todomain/UserResponseMapper.kt @@ -8,4 +8,5 @@ fun UserResponse.toDomain(): Writer = userId = this.userId, nickname = this.nickname, profileImg = this.profileImg, + isAdmin = this.isAdmin, ) diff --git a/core/data/src/main/java/com/example/data/model/response/QuestionPostsResponse.kt b/core/data/src/main/java/com/example/data/model/response/QuestionPostsResponse.kt index 22cfbeaa..8049f968 100644 --- a/core/data/src/main/java/com/example/data/model/response/QuestionPostsResponse.kt +++ b/core/data/src/main/java/com/example/data/model/response/QuestionPostsResponse.kt @@ -1,6 +1,6 @@ package com.example.data.model.response -import com.example.domain.model.BADGE +import com.example.domain.model.Badge import com.example.domain.model.FeedItem import com.example.domain.model.Like import com.example.domain.model.Track @@ -52,7 +52,7 @@ data class QuestionPostItemResponse( postId = postId, isScrapped = isScrapped, content = content, - badge = if (isEditorPick) BADGE.EDITOR else null, + badge = if (isEditorPick) Badge.EDITOR else null, track = Track( trackId = track.trackId, @@ -66,6 +66,7 @@ data class QuestionPostItemResponse( userId = user.userId, nickname = user.nickname, profileImg = user.profileImg.orEmpty(), + isAdmin = user.isAdmin, ), like = Like( diff --git a/core/data/src/main/java/com/example/data/model/response/UserResponse.kt b/core/data/src/main/java/com/example/data/model/response/UserResponse.kt index dc4725ec..9ba74fbf 100644 --- a/core/data/src/main/java/com/example/data/model/response/UserResponse.kt +++ b/core/data/src/main/java/com/example/data/model/response/UserResponse.kt @@ -23,4 +23,6 @@ data class UserResponse( val nickname: String, @SerialName("profileImg") val profileImg: String?, + @SerialName("isAdmin") + val isAdmin: Boolean, ) diff --git a/core/data/src/main/java/com/example/data/repository/PostRepositoryImpl.kt b/core/data/src/main/java/com/example/data/repository/PostRepositoryImpl.kt index 7049511e..ec56595c 100644 --- a/core/data/src/main/java/com/example/data/repository/PostRepositoryImpl.kt +++ b/core/data/src/main/java/com/example/data/repository/PostRepositoryImpl.kt @@ -88,6 +88,7 @@ class PostRepositoryImpl override fun getPostsByQuestionId( questionId: Long, onTotalCountFetched: (Int) -> Unit, + onLockedFetched: (Boolean) -> Unit, ): Flow> = Pager( config = @@ -101,6 +102,7 @@ class PostRepositoryImpl postService = postService, questionId = questionId, onTotalCountFetched = onTotalCountFetched, + onLockedFetched = onLockedFetched, ) }, ).flow.map { pagingData -> diff --git a/core/designsystem/src/main/java/com/example/designsystem/component/DPlayDayTopicItem.kt b/core/designsystem/src/main/java/com/example/designsystem/component/DPlayDayTopicItem.kt index 758530d4..4e69b3e2 100644 --- a/core/designsystem/src/main/java/com/example/designsystem/component/DPlayDayTopicItem.kt +++ b/core/designsystem/src/main/java/com/example/designsystem/component/DPlayDayTopicItem.kt @@ -1,10 +1,15 @@ package com.example.designsystem.component +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -19,31 +24,45 @@ fun DPlayDayTopicItem( onClick: () -> Unit, modifier: Modifier = Modifier, ) { - Row(modifier = modifier.fillMaxWidth().noRippleClickable(onClick = onClick)) { - Text( - text = dayText, + Row( + modifier = + modifier + .fillMaxWidth() + .noRippleClickable(onClick = onClick), + ) { + Box( modifier = Modifier + .size(50.dp) .roundedBackgroundWithPadding( backgroundColor = DPlayTheme.colors.gray600, cornerRadius = 8.dp, - padding = PaddingValues(horizontal = 16.5.dp, vertical = 16.dp), ), - color = DPlayTheme.colors.gray100, - style = DPlayTheme.typography.bodyBold14, - ) - Text( - text = topic, + contentAlignment = Alignment.Center, + ) { + Text( + text = dayText, + color = DPlayTheme.colors.gray100, + style = DPlayTheme.typography.bodyBold14, + ) + } + Box( modifier = Modifier + .height(50.dp) .weight(1f) .roundedBackgroundWithPadding( backgroundColor = DPlayTheme.colors.gray100, cornerRadius = 8.dp, - padding = PaddingValues(horizontal = 10.dp, vertical = 16.dp), + padding = PaddingValues(start = 10.dp), ), - style = DPlayTheme.typography.bodySemi14, - ) + contentAlignment = Alignment.CenterStart, + ) { + Text( + text = topic, + style = DPlayTheme.typography.bodySemi14, + ) + } } } diff --git a/core/designsystem/src/main/java/com/example/designsystem/component/DPlayImageCheck.kt b/core/designsystem/src/main/java/com/example/designsystem/component/DPlayImageCheck.kt index de5342a2..c726f15a 100644 --- a/core/designsystem/src/main/java/com/example/designsystem/component/DPlayImageCheck.kt +++ b/core/designsystem/src/main/java/com/example/designsystem/component/DPlayImageCheck.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage @@ -62,11 +63,15 @@ fun DPlayImageCheck( Spacer(modifier = Modifier.width(12.dp)) - Column { + Column( + modifier = Modifier.weight(1f), + ) { Text( text = musicName, style = DPlayTheme.typography.bodySemi16, color = DPlayTheme.colors.dplayBlack, + maxLines = 1, + overflow = TextOverflow.Ellipsis, ) Spacer(modifier = Modifier.height(4.dp)) @@ -75,11 +80,11 @@ fun DPlayImageCheck( text = artistName, style = DPlayTheme.typography.bodyMed14, color = DPlayTheme.colors.gray400, + maxLines = 1, + overflow = TextOverflow.Ellipsis, ) } - Spacer(modifier = Modifier.weight(1f)) - if (isChecked) { DplayBaseIcon( iconRes = R.drawable.ic_check_circle_darkgray_24, diff --git a/core/designsystem/src/main/java/com/example/designsystem/component/DPlayLargeCover.kt b/core/designsystem/src/main/java/com/example/designsystem/component/DPlayLargeCover.kt index b021ff60..dfd14ee9 100644 --- a/core/designsystem/src/main/java/com/example/designsystem/component/DPlayLargeCover.kt +++ b/core/designsystem/src/main/java/com/example/designsystem/component/DPlayLargeCover.kt @@ -39,6 +39,7 @@ import com.example.designsystem.util.noRippleClickable @Composable fun DPlayLargeCover( isLikeChecked: Boolean, + isAdmin: Boolean, likeCount: Int, writerProfileImageUrl: String?, writerNickname: String, @@ -70,7 +71,12 @@ fun DPlayLargeCover( } } - Box(modifier = modifier.fillMaxWidth().clip(textCoverShape)) { + Box( + modifier = + modifier + .fillMaxWidth() + .clip(textCoverShape), + ) { Box( modifier = Modifier @@ -141,7 +147,12 @@ fun DPlayLargeCover( modifier = Modifier.noRippleClickable(onClick = onWriterProfileClick), ) { AsyncImage( - model = writerProfileImageUrl ?: R.drawable.base_profile_image, + model = + if (isAdmin) { + R.drawable.img_profile + } else { + writerProfileImageUrl ?: R.drawable.base_profile_image + }, contentDescription = null, modifier = Modifier @@ -206,7 +217,7 @@ fun DPlayLargeCover( .background( color = color.dplayWhite, shape = RoundedCornerShape(16.dp), - ).padding(10.dp), + ).padding(6.dp), onClick = onStreamClick, ) } @@ -250,6 +261,7 @@ private fun DPlayLockedLargeCoverPreview() { onLikeClick = {}, onCoverClick = {}, onWriterProfileClick = {}, + isAdmin = false, ) } } @@ -270,6 +282,7 @@ private fun DPlayLargeCoverPreview() { onCoverClick = {}, onWriterProfileClick = {}, isLocked = false, + isAdmin = false, ) } } diff --git a/core/designsystem/src/main/java/com/example/designsystem/component/DPlayTooltip.kt b/core/designsystem/src/main/java/com/example/designsystem/component/DPlayTooltip.kt index c17fce90..2f0a82bf 100644 --- a/core/designsystem/src/main/java/com/example/designsystem/component/DPlayTooltip.kt +++ b/core/designsystem/src/main/java/com/example/designsystem/component/DPlayTooltip.kt @@ -1,5 +1,6 @@ package com.example.designsystem.component +import androidx.annotation.StringRes import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -25,8 +26,9 @@ import com.example.designsystem.util.roundedBackgroundWithPadding @Composable fun DplayTooltip( onCloseButtonClicked: () -> Unit, - onTextButtonClicked: () -> Unit, + onTextButtonClicked: (() -> Unit)?, modifier: Modifier = Modifier, + @StringRes textStringRes: Int = R.string.tooltip_default_description, ) { Column(modifier = modifier) { Box( @@ -63,18 +65,20 @@ fun DplayTooltip( Text( style = DPlayTheme.typography.bodyMed14, color = DPlayTheme.colors.dplayWhite, - text = stringResource(R.string.tooltip_default_description), + text = stringResource(textStringRes), ) DplayClickableIcon( iconRes = R.drawable.ic_close_24, onClick = onCloseButtonClicked, ) } - Spacer(modifier = Modifier.height(16.dp)) - DPlayUnderlineTextButton( - onClick = onTextButtonClicked, - text = stringResource(R.string.tooltip_learn_more), - ) + if (onTextButtonClicked != null) { + Spacer(modifier = Modifier.height(16.dp)) + DPlayUnderlineTextButton( + onClick = onTextButtonClicked, + text = stringResource(R.string.tooltip_learn_more), + ) + } } } } diff --git a/core/designsystem/src/main/java/com/example/designsystem/component/button/DplayGuidelineButton.kt b/core/designsystem/src/main/java/com/example/designsystem/component/button/DplayGuidelineButton.kt index b4feb985..f76b1360 100644 --- a/core/designsystem/src/main/java/com/example/designsystem/component/button/DplayGuidelineButton.kt +++ b/core/designsystem/src/main/java/com/example/designsystem/component/button/DplayGuidelineButton.kt @@ -1,5 +1,6 @@ package com.example.designsystem.component.button +import androidx.annotation.StringRes import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -24,6 +25,7 @@ import com.example.designsystem.theme.DPlayTheme @Composable fun DPlayGuidelineButton( onClick: () -> Unit, + @StringRes textStringRes: Int, modifier: Modifier = Modifier, ) { DPlayButtonSlot( @@ -47,7 +49,7 @@ fun DPlayGuidelineButton( ) Text( - text = stringResource(R.string.guideline_button_label), + text = stringResource(textStringRes), style = DPlayTheme.typography.capMed12, color = DPlayTheme.colors.gray400, ) @@ -69,6 +71,7 @@ fun DPlayGuidelineButtonPreview() { ) { DPlayGuidelineButton( onClick = {}, + textStringRes = R.string.guideline_button_label, ) } } diff --git a/core/designsystem/src/main/res/drawable/img_key.png b/core/designsystem/src/main/res/drawable/img_key.png index b4e34091..91a9ca0b 100644 Binary files a/core/designsystem/src/main/res/drawable/img_key.png and b/core/designsystem/src/main/res/drawable/img_key.png differ diff --git a/core/domain/src/main/java/com/example/domain/model/FeedItem.kt b/core/domain/src/main/java/com/example/domain/model/FeedItem.kt index b01a2d59..f702dfbf 100644 --- a/core/domain/src/main/java/com/example/domain/model/FeedItem.kt +++ b/core/domain/src/main/java/com/example/domain/model/FeedItem.kt @@ -5,13 +5,13 @@ data class FeedItem( val postId: Long, val isScrapped: Boolean, val content: String, - val badge: BADGE?, + val badge: Badge?, val track: Track, val writer: Writer, val like: Like, ) -enum class BADGE { +enum class Badge { EDITOR, BEST, NEW, diff --git a/core/domain/src/main/java/com/example/domain/model/UserRelation.kt b/core/domain/src/main/java/com/example/domain/model/UserRelation.kt index 7564ad27..53e050ce 100644 --- a/core/domain/src/main/java/com/example/domain/model/UserRelation.kt +++ b/core/domain/src/main/java/com/example/domain/model/UserRelation.kt @@ -2,6 +2,5 @@ package com.example.domain.model enum class UserRelation { ME, - ADMIN, OTHER; } \ No newline at end of file diff --git a/core/domain/src/main/java/com/example/domain/model/Writer.kt b/core/domain/src/main/java/com/example/domain/model/Writer.kt index 1bb7dcb5..054ac0a7 100644 --- a/core/domain/src/main/java/com/example/domain/model/Writer.kt +++ b/core/domain/src/main/java/com/example/domain/model/Writer.kt @@ -4,4 +4,5 @@ data class Writer( val userId: Long, val nickname: String, val profileImg: String?, + val isAdmin: Boolean, ) diff --git a/core/domain/src/main/java/com/example/domain/repository/PostRepository.kt b/core/domain/src/main/java/com/example/domain/repository/PostRepository.kt index cc37996f..e5d1d7d7 100644 --- a/core/domain/src/main/java/com/example/domain/repository/PostRepository.kt +++ b/core/domain/src/main/java/com/example/domain/repository/PostRepository.kt @@ -41,5 +41,6 @@ interface PostRepository { fun getPostsByQuestionId( questionId: Long, onTotalCountFetched: (Int) -> Unit, + onLockedFetched: (Boolean) -> Unit, ): Flow> } \ No newline at end of file diff --git a/core/domain/src/main/java/com/example/domain/usecase/CheckUserRelationUseCase.kt b/core/domain/src/main/java/com/example/domain/usecase/CheckUserRelationUseCase.kt index ceab1f44..e629bf89 100644 --- a/core/domain/src/main/java/com/example/domain/usecase/CheckUserRelationUseCase.kt +++ b/core/domain/src/main/java/com/example/domain/usecase/CheckUserRelationUseCase.kt @@ -11,13 +11,8 @@ class CheckUserRelationUseCase @Inject constructor( suspend operator fun invoke(userId: Long): UserRelation { val myId = userRepository.getUser().first()?.id return when { - userId == ADMIN_ID -> UserRelation.ADMIN myId == userId -> UserRelation.ME else -> UserRelation.OTHER } } - - companion object{ - const val ADMIN_ID = 1L - } } \ No newline at end of file diff --git a/core/navigation/src/main/java/com/example/navigation/Route.kt b/core/navigation/src/main/java/com/example/navigation/Route.kt index e90ea58a..61e2cad4 100644 --- a/core/navigation/src/main/java/com/example/navigation/Route.kt +++ b/core/navigation/src/main/java/com/example/navigation/Route.kt @@ -3,7 +3,7 @@ package com.example.navigation import androidx.annotation.DrawableRes import androidx.navigation3.runtime.NavKey import com.dplay.designsystem.R -import com.example.domain.model.BADGE +import com.example.domain.model.Badge import com.example.ui.model.TrackState import kotlinx.serialization.Serializable @@ -71,7 +71,7 @@ data object Record : NavKey @Serializable data class Detail( val postId: Long, - val badge: BADGE? = null, + val badge: Badge? = null, ) : NavKey @Serializable diff --git a/feature/comment/src/main/java/com/example/comment/CommentScreen.kt b/feature/comment/src/main/java/com/example/comment/CommentScreen.kt index 1ebacc78..ab484697 100644 --- a/feature/comment/src/main/java/com/example/comment/CommentScreen.kt +++ b/feature/comment/src/main/java/com/example/comment/CommentScreen.kt @@ -157,6 +157,7 @@ fun CommentScreen( .onGloballyPositioned { coordinates -> guideButtonHeightPx = coordinates.size.height }, + textStringRes = R.string.guideline_button_label, ) if (state.isGuideVisible) { diff --git a/feature/detail/src/main/java/com/example/detail/DetailContract.kt b/feature/detail/src/main/java/com/example/detail/DetailContract.kt index 68cabeca..64fb6210 100644 --- a/feature/detail/src/main/java/com/example/detail/DetailContract.kt +++ b/feature/detail/src/main/java/com/example/detail/DetailContract.kt @@ -1,7 +1,7 @@ package com.example.detail import com.example.designsystem.component.snackbar.type.SnackBarType -import com.example.domain.model.BADGE +import com.example.domain.model.Badge import com.example.domain.model.Like import com.example.domain.model.LoadingState import com.example.domain.model.Track @@ -14,6 +14,7 @@ class DetailContract { val loadingState: LoadingState = LoadingState.LOADING, val postId: Long = 0L, val isScrapped: Boolean = false, + val initialIsScrapped: Boolean = false, val content: String = "", val isHost: Boolean = false, val date: String = "", @@ -30,21 +31,26 @@ class DetailContract { userId = 0, nickname = "", profileImg = "", + isAdmin = false, ), val like: Like = Like( isLiked = false, count = 0, ), - val badge: BADGE? = null, + val initialIsLiked: Boolean = false, + val badge: Badge? = null, val bottomSheetVisible: Boolean = false, val streamingTrackId: String? = null, - ) : BaseContract.State + ) : BaseContract.State { + val homeRefreshRequired: Boolean + get() = isScrapped != initialIsScrapped || like.isLiked != initialIsLiked + } sealed interface DetailIntent : BaseContract.Intent { data class LoadData( val postId: Long, - val badge: BADGE? = null, + val badge: Badge? = null, ) : DetailIntent data object OnBookmarkClick : DetailIntent diff --git a/feature/detail/src/main/java/com/example/detail/DetailScreen.kt b/feature/detail/src/main/java/com/example/detail/DetailScreen.kt index ca0e1805..96f76e48 100644 --- a/feature/detail/src/main/java/com/example/detail/DetailScreen.kt +++ b/feature/detail/src/main/java/com/example/detail/DetailScreen.kt @@ -1,5 +1,6 @@ package com.example.detail +import androidx.activity.compose.BackHandler import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -24,8 +25,8 @@ 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.blur import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview @@ -48,7 +49,7 @@ import com.example.designsystem.component.snackbar.LocalShowSnackBar import com.example.designsystem.theme.DPlayTheme import com.example.designsystem.util.noRippleClickable import com.example.designsystem.util.roundedBackgroundWithPadding -import com.example.domain.model.BADGE +import com.example.domain.model.Badge import com.example.domain.model.LoadingState import com.example.navigation.MyPage import com.example.navigation.Navigator @@ -61,12 +62,16 @@ fun DetailRoute( postId: Long, navigator: Navigator, viewModel: DetailViewModel = hiltViewModel(), - badge: BADGE? = null, + badge: Badge? = null, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() val showSnackBar = LocalShowSnackBar.current val modalController = LocalModalController.current + BackHandler { + viewModel.handleIntent(DetailContract.DetailIntent.OnBackButtonClick) + } + LaunchedEffect(Unit) { viewModel.handleIntent(DetailContract.DetailIntent.LoadData(postId = postId, badge = badge)) } @@ -174,13 +179,8 @@ private fun DetailScreen( .padding(horizontal = 16.dp) Box { - Box(modifier = Modifier.fillMaxSize()) { - Box( - modifier = - Modifier.blur( - radius = 20.dp, - ), - ) { + Box(modifier = Modifier.fillMaxSize().background(color = color.gray100)) { + Box { AsyncImage( model = state.track.coverImg, contentDescription = null, @@ -191,6 +191,23 @@ private fun DetailScreen( .offset(y = (-80).dp), contentScale = ContentScale.Crop, ) + Box( + modifier = + Modifier + .fillMaxWidth() + .aspectRatio(1f) + .offset(y = (-80).dp) + .background( + brush = + Brush.verticalGradient( + colors = + listOf( + DPlayTheme.colors.gray100.copy(alpha = 0f), + DPlayTheme.colors.gray100.copy(alpha = 1f), + ), + ), + ), + ) } Column { DplayDualIconTitleTopAppBar( @@ -216,9 +233,9 @@ private fun DetailScreen( state.badge?.let { badge -> val chipType = when (badge) { - BADGE.BEST -> DPlayChipType.BEST - BADGE.EDITOR -> DPlayChipType.EDITOR - BADGE.NEW -> DPlayChipType.NEW + Badge.BEST -> DPlayChipType.BEST + Badge.EDITOR -> DPlayChipType.EDITOR + Badge.NEW -> DPlayChipType.NEW } Image( painter = painterResource(id = chipType.drawableRes), @@ -234,14 +251,14 @@ private fun DetailScreen( Text( text = state.track.songTitle, - modifier = Modifier.align(Alignment.CenterHorizontally), + modifier = Modifier.padding(horizontal = 16.dp).align(Alignment.CenterHorizontally), style = typography.bodyBold20, color = color.dplayBlack, ) Spacer(modifier = Modifier.height(4.dp)) Text( text = state.track.artistName, - modifier = Modifier.align(Alignment.CenterHorizontally), + modifier = Modifier.padding(horizontal = 16.dp).align(Alignment.CenterHorizontally), style = typography.bodySemi14, color = color.gray400, ) @@ -290,7 +307,12 @@ private fun DetailScreen( verticalAlignment = Alignment.CenterVertically, ) { AsyncImage( - model = state.writer.profileImg ?: R.drawable.base_profile_image, + model = + if (state.writer.isAdmin) { + R.drawable.img_profile + } else { + state.writer.profileImg ?: R.drawable.base_profile_image + }, contentDescription = null, modifier = Modifier diff --git a/feature/detail/src/main/java/com/example/detail/DetailViewModel.kt b/feature/detail/src/main/java/com/example/detail/DetailViewModel.kt index 8b20dcd3..1c0cec66 100644 --- a/feature/detail/src/main/java/com/example/detail/DetailViewModel.kt +++ b/feature/detail/src/main/java/com/example/detail/DetailViewModel.kt @@ -8,7 +8,7 @@ import com.example.common.event.ScrappedTrackRefreshTrigger import com.example.designsystem.component.snackbar.type.SnackBarType import com.example.detail.DetailContract.DetailSideEffect.NavigateToMyPage import com.example.detail.DetailContract.DetailSideEffect.ShowSnackBar -import com.example.domain.model.BADGE +import com.example.domain.model.Badge import com.example.domain.model.Like import com.example.domain.model.LoadingState import com.example.domain.model.UserRelation @@ -62,6 +62,9 @@ class DetailViewModel when (intent) { is DetailContract.DetailIntent.LoadData -> loadData(intent.postId, intent.badge) is DetailContract.DetailIntent.OnBackButtonClick -> { + if (currentState.homeRefreshRequired) { + viewModelScope.launch { homeRefreshTrigger.refresh() } + } setSideEffect(DetailContract.DetailSideEffect.NavigateBackStack) } @@ -80,7 +83,7 @@ class DetailViewModel is DetailContract.DetailIntent.OnReportClick -> reportPost() is DetailContract.DetailIntent.OnStreamClick -> streamTrack() is DetailContract.DetailIntent.OnWriterProfileClick -> { - navigateToOthersProfile() + if (!currentState.writer.isAdmin) navigateToOthersProfile() } is DetailContract.DetailIntent.ChangeBottomSheetVisible -> { @@ -91,7 +94,7 @@ class DetailViewModel private fun loadData( postId: Long, - badge: BADGE?, + badge: Badge?, ) { viewModelScope.launch { postRepository @@ -102,12 +105,14 @@ class DetailViewModel loadingState = LoadingState.SUCCESS, postId = postDetail.postId, isScrapped = postDetail.isScrapped, + initialIsScrapped = postDetail.isScrapped, content = postDetail.content, isHost = postDetail.isHost, date = postDetail.displayDate, track = postDetail.track, writer = postDetail.writer, like = postDetail.like, + initialIsLiked = postDetail.like.isLiked, badge = badge, ) } @@ -236,7 +241,6 @@ class DetailViewModel when (userRelation) { UserRelation.ME -> setSideEffect(NavigateToMyPage()) - UserRelation.ADMIN -> {} UserRelation.OTHER -> setSideEffect(DetailContract.DetailSideEffect.NavigateToWriterProfile(userId)) } } diff --git a/feature/editprofile/src/main/java/com/example/editprofile/EditProfileScreen.kt b/feature/editprofile/src/main/java/com/example/editprofile/EditProfileScreen.kt index 31c2f5c1..aa2e59f1 100644 --- a/feature/editprofile/src/main/java/com/example/editprofile/EditProfileScreen.kt +++ b/feature/editprofile/src/main/java/com/example/editprofile/EditProfileScreen.kt @@ -162,7 +162,7 @@ fun EditProfileScreen( onFocusChange = {}, placeholder = stringResource(R.string.placeholder_nickname), maxLength = TextFieldConstant.MAX_NICKNAME_LENGTH, - modifier = Modifier.padding(horizontal = 8.dp), + modifier = Modifier.padding(horizontal = 16.dp), ) Spacer(modifier = Modifier.weight(1f)) diff --git a/feature/home/src/main/java/com/example/home/HomeContract.kt b/feature/home/src/main/java/com/example/home/HomeContract.kt index 1ed785d0..e9422eb3 100644 --- a/feature/home/src/main/java/com/example/home/HomeContract.kt +++ b/feature/home/src/main/java/com/example/home/HomeContract.kt @@ -1,7 +1,7 @@ package com.example.home import com.example.designsystem.component.snackbar.type.SnackBarType -import com.example.domain.model.BADGE +import com.example.domain.model.Badge import com.example.domain.model.DailyQuestion import com.example.domain.model.FeedItem import com.example.navigation.MyPageTab @@ -62,7 +62,7 @@ class HomeContract { data class NavigateToPostDetail( val postId: Long, - val badge: BADGE?, + val badge: Badge?, ) : HomeSideEffect data object NavigateToRecord : HomeSideEffect diff --git a/feature/home/src/main/java/com/example/home/HomeScreen.kt b/feature/home/src/main/java/com/example/home/HomeScreen.kt index b727bd30..0d87b316 100644 --- a/feature/home/src/main/java/com/example/home/HomeScreen.kt +++ b/feature/home/src/main/java/com/example/home/HomeScreen.kt @@ -34,7 +34,7 @@ import com.example.designsystem.component.button.DPlayBookmarkButton import com.example.designsystem.component.chip.type.DPlayChipType import com.example.designsystem.component.snackbar.LocalShowSnackBar import com.example.designsystem.theme.DPlayTheme -import com.example.domain.model.BADGE +import com.example.domain.model.Badge import com.example.domain.model.FeedItem import com.example.navigation.Detail import com.example.navigation.MyPage @@ -210,9 +210,9 @@ private fun HomePager( val currentChipType: DPlayChipType? = currentItem?.badge?.let { when (it) { - BADGE.BEST -> DPlayChipType.BEST - BADGE.EDITOR -> DPlayChipType.EDITOR - BADGE.NEW -> DPlayChipType.NEW + Badge.BEST -> DPlayChipType.BEST + Badge.EDITOR -> DPlayChipType.EDITOR + Badge.NEW -> DPlayChipType.NEW } } @@ -230,6 +230,7 @@ private fun HomePager( DPlayLargeCover( modifier = Modifier.fillMaxWidth(), + isAdmin = item.writer.isAdmin, isLocked = isLockedPage, isLikeChecked = item.like.isLiked, likeCount = item.like.count, @@ -246,7 +247,9 @@ private fun HomePager( onPostClick(item.postId) } }, - onWriterProfileClick = { onWriterProfileClick(item.writer.userId) }, + onWriterProfileClick = { + if (!item.writer.isAdmin) onWriterProfileClick(item.writer.userId) + }, isStreaming = uiState.streamingTrackId == item.track.trackId, ) } diff --git a/feature/home/src/main/java/com/example/home/HomeViewModel.kt b/feature/home/src/main/java/com/example/home/HomeViewModel.kt index fe4f5f15..ac73bca6 100644 --- a/feature/home/src/main/java/com/example/home/HomeViewModel.kt +++ b/feature/home/src/main/java/com/example/home/HomeViewModel.kt @@ -4,7 +4,6 @@ import androidx.lifecycle.viewModelScope import com.example.common.audio.AudioPlayer import com.example.common.event.HomeRefreshTrigger import com.example.designsystem.component.snackbar.type.SnackBarType -import com.example.domain.model.BADGE import com.example.domain.model.FeedItem import com.example.domain.model.Like import com.example.domain.model.Track @@ -16,7 +15,6 @@ import com.example.domain.usecase.CheckUserRelationUseCase import com.example.navigation.MyPageTab import com.example.ui.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -136,6 +134,7 @@ class HomeViewModel userId = -1L, nickname = "", profileImg = "", + isAdmin = false, ), like = Like( @@ -266,163 +265,8 @@ class HomeViewModel when (userRelation) { UserRelation.ME -> setSideEffect(HomeContract.HomeSideEffect.NavigateToMyPage()) - UserRelation.ADMIN -> {} UserRelation.OTHER -> setSideEffect(HomeContract.HomeSideEffect.NavigateToWriterProfile(userId)) } } } } - -val dummyFeedItems = - persistentListOf( - FeedItem( - postId = 111, - isScrapped = true, - content = "그냥 좋아요 이 노래", - badge = BADGE.BEST, - track = - Track( - trackId = "apple:203948", - songTitle = "Song Title 1", - coverImg = "https://picsum.photos/300", - artistName = "Artist1, Artist2", - isrc = "USUC1234567890", - ), - writer = - Writer( - userId = 222, - nickname = "윤서암", - profileImg = "https://picsum.photos/200", - ), - like = - Like( - isLiked = false, - count = 24, - ), - ), - FeedItem( - postId = 112, - isScrapped = false, - content = "비 오는 날 꼭 듣는 노래에요", - badge = BADGE.EDITOR, - track = - Track( - trackId = "apple:204837", - songTitle = "Song Title 2", - coverImg = "https://picsum.photos/310", - artistName = "Artist3", - isrc = "USUC1234567891", - ), - writer = - Writer( - userId = 333, - nickname = "민석", - profileImg = "https://picsum.photos/201", - ), - like = - Like( - isLiked = true, - count = 57, - ), - ), - FeedItem( - postId = 113, - isScrapped = false, - content = "출근길에 항상 듣습니다!", - badge = BADGE.NEW, - track = - Track( - trackId = "apple:204111", - songTitle = "Song Title 3", - coverImg = "https://picsum.photos/320", - artistName = "Artist4", - isrc = "USUC1234567892", - ), - writer = - Writer( - userId = 444, - nickname = "서현", - profileImg = "https://picsum.photos/202", - ), - like = - Like( - isLiked = false, - count = 13, - ), - ), - FeedItem( - postId = 113, - isScrapped = false, - content = "출근길에 항상 듣습니다!", - badge = BADGE.NEW, - track = - Track( - trackId = "apple:204111", - songTitle = "Song Title 3", - coverImg = "https://picsum.photos/320", - artistName = "Artist4", - isrc = "USUC1234567893", - ), - writer = - Writer( - userId = 444, - nickname = "서현", - profileImg = "https://picsum.photos/202", - ), - like = - Like( - isLiked = false, - count = 13, - ), - ), - FeedItem( - postId = 113, - isScrapped = false, - content = "출근길에 항상 듣습니다!", - badge = BADGE.NEW, - track = - Track( - trackId = "apple:204111", - songTitle = "Song Title 3", - coverImg = "https://picsum.photos/320", - artistName = "Artist4", - isrc = "USUC1234567894", - ), - writer = - Writer( - userId = 444, - nickname = "서현", - profileImg = "https://picsum.photos/202", - ), - like = - Like( - isLiked = false, - count = 13, - ), - ), - FeedItem( - postId = 113, - isScrapped = false, - content = "출근길에 항상 듣습니다!", - badge = BADGE.NEW, - track = - Track( - trackId = "apple:204111", - songTitle = "Song Title 3", - coverImg = "https://picsum.photos/320", - artistName = "Artist4", - isrc = "USUC1234567895", - ), - writer = - Writer( - userId = 444, - nickname = "서현", - profileImg = "https://picsum.photos/202", - ), - like = - Like( - isLiked = false, - count = 13, - ), - ), - ) diff --git a/feature/onboarding/src/main/java/com/example/onboarding/OnboardingProfileScreen.kt b/feature/onboarding/src/main/java/com/example/onboarding/OnboardingProfileScreen.kt index 9b71d012..c3565f9a 100644 --- a/feature/onboarding/src/main/java/com/example/onboarding/OnboardingProfileScreen.kt +++ b/feature/onboarding/src/main/java/com/example/onboarding/OnboardingProfileScreen.kt @@ -172,7 +172,7 @@ fun OnboardingProfileScreen( onFocusChange = {}, placeholder = stringResource(R.string.placeholder_nickname), maxLength = TextFieldConstant.MAX_NICKNAME_LENGTH, - modifier = Modifier.padding(horizontal = 8.dp), + modifier = Modifier.padding(horizontal = 16.dp), ) Spacer(modifier = Modifier.weight(1f)) diff --git a/feature/record/src/main/java/com/example/record/RecordContract.kt b/feature/record/src/main/java/com/example/record/RecordContract.kt index 86d3145c..d0107007 100644 --- a/feature/record/src/main/java/com/example/record/RecordContract.kt +++ b/feature/record/src/main/java/com/example/record/RecordContract.kt @@ -14,6 +14,8 @@ class RecordContract { val selectedQuestion: DailyQuestion? = null, val datePickerBottomSheetVisible: Boolean = false, val recordListTotalCount: Int = 0, + val tooltipVisible: Boolean = false, + val locked: Boolean = true, ) : BaseContract.State sealed interface RecordIntent : BaseContract.Intent { @@ -37,6 +39,10 @@ class RecordContract { val year: Int, val month: Int, ) : RecordIntent + + data class ChangeTooltipVisible( + val isVisible: Boolean, + ) : RecordIntent } sealed interface RecordSideEffect : BaseContract.SideEffect { diff --git a/feature/record/src/main/java/com/example/record/RecordListScreen.kt b/feature/record/src/main/java/com/example/record/RecordListScreen.kt index 3891a74e..0bb60a87 100644 --- a/feature/record/src/main/java/com/example/record/RecordListScreen.kt +++ b/feature/record/src/main/java/com/example/record/RecordListScreen.kt @@ -1,5 +1,6 @@ package com.example.record +import androidx.activity.compose.BackHandler import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -20,8 +21,10 @@ import com.dplay.record.R import com.example.designsystem.component.DPlayMusicListItem import com.example.designsystem.component.DPlaySubjectItem import com.example.designsystem.component.DplayLeftIconTitleTopAppBar +import com.example.designsystem.component.DplayTooltip +import com.example.designsystem.component.button.DPlayGuidelineButton import com.example.designsystem.theme.DPlayTheme -import com.example.domain.model.BADGE +import com.example.domain.model.Badge import com.example.domain.model.FeedItem import com.example.ui.emptyLazyPagingItems @@ -29,10 +32,15 @@ import com.example.ui.emptyLazyPagingItems fun RecordListScreen( onBackButtonClick: () -> Unit, onMusicClick: (postId: Long) -> Unit, + onGuideButtonClick: () -> Unit, + onTooltipCloseClick: () -> Unit, modifier: Modifier = Modifier, uiState: RecordContract.RecordState = RecordContract.RecordState(), questionPosts: LazyPagingItems = emptyLazyPagingItems(), ) { + BackHandler { + onBackButtonClick() + } Column(modifier = modifier.fillMaxSize()) { DplayLeftIconTitleTopAppBar( modifier = Modifier.fillMaxWidth(), @@ -71,10 +79,27 @@ fun RecordListScreen( musicName = item.track.songTitle, musicArtistName = item.track.artistName, musicContent = item.content, - isEditorPick = (item.badge == BADGE.EDITOR), + isEditorPick = (item.badge == Badge.EDITOR), onClick = { onMusicClick(item.postId) }, ) } + + if (uiState.locked) { + item { + DPlayGuidelineButton( + onClick = onGuideButtonClick, + textStringRes = R.string.record_locked_guide_button_text, + ) + if (uiState.tooltipVisible) { + Spacer(modifier = Modifier.height(8.dp)) + DplayTooltip( + onCloseButtonClicked = onTooltipCloseClick, + textStringRes = R.string.record_locked_tooltip_description, + onTextButtonClicked = null, + ) + } + } + } } } } @@ -86,6 +111,8 @@ private fun RecordListScreenPreview() { RecordListScreen( onBackButtonClick = {}, onMusicClick = {}, + onGuideButtonClick = {}, + onTooltipCloseClick = {}, ) } } diff --git a/feature/record/src/main/java/com/example/record/RecordRoute.kt b/feature/record/src/main/java/com/example/record/RecordRoute.kt index ab83cd12..3603f366 100644 --- a/feature/record/src/main/java/com/example/record/RecordRoute.kt +++ b/feature/record/src/main/java/com/example/record/RecordRoute.kt @@ -53,6 +53,12 @@ fun RecordRoute( onMusicClick = { postId -> viewModel.handleIntent(RecordContract.RecordIntent.OnMusicClick(postId = postId)) }, + onGuideButtonClick = { + viewModel.handleIntent(RecordContract.RecordIntent.ChangeTooltipVisible(isVisible = true)) + }, + onTooltipCloseClick = { + viewModel.handleIntent(RecordContract.RecordIntent.ChangeTooltipVisible(isVisible = false)) + }, ) } } diff --git a/feature/record/src/main/java/com/example/record/RecordViewModel.kt b/feature/record/src/main/java/com/example/record/RecordViewModel.kt index 83ac21f3..e12189f1 100644 --- a/feature/record/src/main/java/com/example/record/RecordViewModel.kt +++ b/feature/record/src/main/java/com/example/record/RecordViewModel.kt @@ -43,6 +43,9 @@ class RecordViewModel onTotalCountFetched = { totalCount -> updateState { copy(recordListTotalCount = totalCount) } }, + onLockedFetched = { locked -> + updateState { copy(locked = locked) } + }, ) } else { flowOf(PagingData.empty()) @@ -51,6 +54,7 @@ class RecordViewModel init { val now = YearMonth.now() + setDate(year = now.year, month = now.month.value) loadQuestions(year = now.year, month = now.month.value) } @@ -70,6 +74,10 @@ class RecordViewModel is RecordContract.RecordIntent.ChangeBottomSheetVisible -> { updateState { copy(datePickerBottomSheetVisible = intent.isVisible) } } + + is RecordContract.RecordIntent.ChangeTooltipVisible -> { + updateState { copy(tooltipVisible = intent.isVisible) } + } } } diff --git a/feature/record/src/main/res/values/strings.xml b/feature/record/src/main/res/values/strings.xml index 1b08b1e8..47d26ae9 100644 --- a/feature/record/src/main/res/values/strings.xml +++ b/feature/record/src/main/res/values/strings.xml @@ -1,4 +1,6 @@ 총 %1$d개의 곡 + 이 날의 추천은 여기까지에요.\n곡을 등록하지 않은 날에는 최대 3곡만 보여요. + 왜 일부 노래만 보이나요? \ No newline at end of file