diff --git a/app/src/main/java/com/flint/domain/model/user/KeywordListModel.kt b/app/src/main/java/com/flint/domain/model/user/KeywordListModel.kt index 0cc2c890..876ed9f0 100644 --- a/app/src/main/java/com/flint/domain/model/user/KeywordListModel.kt +++ b/app/src/main/java/com/flint/domain/model/user/KeywordListModel.kt @@ -11,34 +11,40 @@ data class KeywordListModel( val FakeList1 = KeywordListModel( keywords = persistentListOf( KeywordItemModel( - name = "추리", + name = "애니메이션", color = "BLUE", - rank = 1 + rank = 1, + percentage = 75f, ), KeywordItemModel( name = "슬픈", color = "GREEN", - rank = 4 + rank = 4, + percentage = 35f, ), KeywordItemModel( name = "SF", color = "PINK", rank = 2, + percentage = 60f, ), KeywordItemModel( name = "액션", color = "ORANGE", rank = 5, + percentage = 25f, ), KeywordItemModel( - name = "슬픈", + name = "몽환적인", color = "YELLOW", rank = 3, + percentage = 48f, ), KeywordItemModel( name = "성장", color = "YELLOW", rank = 6, + percentage = 15f, ), ) ) diff --git a/app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt b/app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt index f9b0f971..be81142f 100644 --- a/app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt +++ b/app/src/main/java/com/flint/presentation/profile/ProfileScreen.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.ui.Alignment import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -32,13 +31,17 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.flint.core.common.extension.findActivity +import com.flint.core.common.extension.noRippleClickable +import com.flint.R +import androidx.compose.material3.Icon +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource import com.flint.core.common.util.UiState import com.flint.core.designsystem.component.bottomsheet.OttListBottomSheet import com.flint.core.designsystem.component.indicator.FlintLoadingIndicator import com.flint.core.designsystem.component.listView.CollectionSection import com.flint.core.designsystem.component.listView.SavedContentsSection -import com.flint.core.designsystem.component.topappbar.FlintBackTopAppbar -import com.flint.core.designsystem.theme.Colors +import com.flint.core.designsystem.component.topappbar.FlintBasicTopAppbar import com.flint.core.designsystem.theme.FlintTheme import com.flint.core.designsystem.theme.FlintTheme.colors import com.flint.core.navigation.model.CollectionListRouteType @@ -138,6 +141,7 @@ private fun ProfileScreen( modifier: Modifier = Modifier, onRefreshClick: () -> Unit = {}, onBackClick: () -> Unit = {}, + onSettingsClick: () -> Unit = {}, onCollectionItemClick: (collectionId: String) -> Unit, onContentItemClick: (contentId: String) -> Unit = {}, onContentMoreClick: () -> Unit = {}, @@ -149,6 +153,7 @@ private fun ProfileScreen( var topHeightPx by remember { mutableIntStateOf(0) } val density = LocalDensity.current val topHeightDp = with(density) { topHeightPx.toDp() } + var showInfoModal by remember { mutableStateOf(false) } Box( modifier = modifier @@ -198,6 +203,9 @@ private fun ProfileScreen( ProfileKeywordSection( nickname = uiState.profile.nickname, keywordList = sectionData.data.keywords, + isMyProfile = uiState.userId == null, + showInfoModal = showInfoModal, + onInfoClick = { showInfoModal = !showInfoModal }, onRefreshClick = onRefreshClick, modifier = Modifier.fillMaxWidth(), ) @@ -208,7 +216,7 @@ private fun ProfileScreen( Spacer(Modifier.height(48.dp)) CollectionSection( - title = "생성한 컬렉션", + title = "${userName}님의 컬렉션", description = "${userName}님이 생성한 컬렉션이에요", onItemClick = onCollectionItemClick, isAllVisible = true, @@ -250,11 +258,36 @@ private fun ProfileScreen( else -> {} } } - if (uiState.userId != null) { - FlintBackTopAppbar( - onClick = onBackClick, + if (showInfoModal) { + Box( + modifier = Modifier + .fillMaxSize() + .noRippleClickable { showInfoModal = false }, ) } + FlintBasicTopAppbar( + backgroundColor = Color.Transparent, + navigationIcon = { + if (uiState.userId != null) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_back), + contentDescription = null, + tint = FlintTheme.colors.white, + modifier = Modifier.noRippleClickable { onBackClick() }, + ) + } + }, + action = { + if (uiState.userId == null) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_setting), + contentDescription = "설정", + tint = FlintTheme.colors.white, + modifier = Modifier.noRippleClickable { onSettingsClick() }, + ) + } + }, + ) } } diff --git a/app/src/main/java/com/flint/presentation/profile/component/InfoModalTrigger.kt b/app/src/main/java/com/flint/presentation/profile/component/InfoModalTrigger.kt new file mode 100644 index 00000000..cd021018 --- /dev/null +++ b/app/src/main/java/com/flint/presentation/profile/component/InfoModalTrigger.kt @@ -0,0 +1,44 @@ +package com.flint.presentation.profile.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.flint.core.designsystem.theme.FlintTheme + +@Composable +fun InfoModalTrigger( + text: String, + modifier: Modifier = Modifier, +) { + Box( + contentAlignment = Alignment.Center, + modifier = modifier + .background( + color = FlintTheme.colors.gray800, + shape = RoundedCornerShape(size = 12.dp), + ) + .padding(horizontal = 12.dp, vertical = 14.dp), + ) { + Text( + text = text, + style = FlintTheme.typography.body2R14, + color = FlintTheme.colors.gray300, + ) + } +} + +@Preview(showBackground = true, backgroundColor = 0xFF141417) +@Composable +private fun InfoModalTriggerPreview() { + FlintTheme { + InfoModalTrigger(text = "저장한 작품들에서 반복되는 키워드를 분석해 취향 키워드를 만들어요. n개 이상 작품이 쌓이면 업데이트할 수 있어요.") + } +} diff --git a/app/src/main/java/com/flint/presentation/profile/component/ProfileKeywordGraphItem.kt b/app/src/main/java/com/flint/presentation/profile/component/ProfileKeywordGraphItem.kt index c3af37e6..ee7797f6 100644 --- a/app/src/main/java/com/flint/presentation/profile/component/ProfileKeywordGraphItem.kt +++ b/app/src/main/java/com/flint/presentation/profile/component/ProfileKeywordGraphItem.kt @@ -1,6 +1,7 @@ package com.flint.presentation.profile.component import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row @@ -20,6 +21,9 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -80,20 +84,48 @@ private fun ProfileKeywordProgressBar( percent: Float, modifier: Modifier = Modifier, ) { + val safePercent = percent.coerceIn(0f, 1f) + val shape = RoundedCornerShape(4.dp) + + // Track: #4F5669, stop 20%→100% alpha, 레이어 전체 44% opacity + val trackBrush = Brush.linearGradient( + colors = listOf( + Color(0xFF4F5669).copy(alpha = 0.20f * 0.44f), + Color(0xFF4F5669).copy(alpha = 0.44f), + ) + ) + + // Fill: 키워드 색상, stop 20%→100% alpha + val fillBrush = Brush.linearGradient( + colors = listOf( + preferenceType.color.copy(alpha = 0.20f), + preferenceType.color, + ) + ) + + // Border: white→transparent, 상→하 방향 (유리 효과 - 오른쪽 끝까지 border 보임) + val borderBrush = Brush.linearGradient( + colors = listOf( + Color.White.copy(alpha = 0.30f), + Color.Transparent, + ), + start = Offset(0f, 0f), + end = Offset(0f, Float.POSITIVE_INFINITY), + ) + Box( - modifier = - modifier - .height(12.dp) - .clip(RoundedCornerShape(4.dp)) - .background(FlintTheme.colors.gray500), + modifier = modifier + .height(12.dp) + .background(trackBrush, shape) + .border(1.dp, borderBrush, shape) + .clip(shape), ) { Box( - modifier = - Modifier - .fillMaxWidth(percent) - .fillMaxHeight() - .clip(RoundedCornerShape(4.dp)) - .background(preferenceType.color), + modifier = Modifier + .fillMaxWidth(safePercent) + .fillMaxHeight() + .clip(shape) + .background(fillBrush), ) } } diff --git a/app/src/main/java/com/flint/presentation/profile/component/ProfileKeywordSection.kt b/app/src/main/java/com/flint/presentation/profile/component/ProfileKeywordSection.kt index 6ba4d528..9090f308 100644 --- a/app/src/main/java/com/flint/presentation/profile/component/ProfileKeywordSection.kt +++ b/app/src/main/java/com/flint/presentation/profile/component/ProfileKeywordSection.kt @@ -1,6 +1,8 @@ package com.flint.presentation.profile.component import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -8,13 +10,19 @@ 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.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -33,9 +41,13 @@ import kotlinx.collections.immutable.toPersistentList fun ProfileKeywordSection( nickname: String, keywordList: KeywordListModel, + isMyProfile: Boolean, + showInfoModal: Boolean, + onInfoClick: () -> Unit, onRefreshClick: () -> Unit, modifier: Modifier = Modifier, ) { + Column( modifier = modifier @@ -49,11 +61,24 @@ fun ProfileKeywordSection( modifier = Modifier.fillMaxWidth(), ) { Column { - Text( - text = "${nickname}님의 취향키워드", - style = FlintTheme.typography.head3Sb18, - color = FlintTheme.colors.white, - ) + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + text = "${nickname}님의 취향키워드", + style = FlintTheme.typography.head3Sb18, + color = FlintTheme.colors.white, + ) + if (isMyProfile) { + Spacer(Modifier.width(4.dp)) + Icon( + painter = painterResource(R.drawable.ic_info), + contentDescription = "취향키워드 정보", + tint = FlintTheme.colors.gray300, + modifier = Modifier + .size(20.dp) + .noRippleClickable { onInfoClick() }, + ) + } + } Spacer(Modifier.height(4.dp)) Text( text = "${nickname}님이 관심있어하는 키워드예요", @@ -61,9 +86,28 @@ fun ProfileKeywordSection( color = FlintTheme.colors.gray100, ) } + if (isMyProfile) { + ProfileRefreshButton(onRefreshClick = onRefreshClick) + } } Spacer(Modifier.height(32.dp)) - KeywordChipsGridLayout( + Box { + KeywordChipsGridLayout( + keywordList = keywordList.keywords, + modifier = Modifier.fillMaxWidth(), + ) + if (isMyProfile && showInfoModal) { + InfoModalTrigger( + text = "저장한 작품들에서 반복되는 키워드를 분석해 취향 키워드를 만들어요. 10개 이상 작품이 쌓이면 업데이트할 수 있어요.", + modifier = Modifier + .fillMaxWidth() + .offset(y = (-20).dp), + ) + } + } + + Spacer(Modifier.height(32.dp)) + KeywordGraphLayout( keywordList = keywordList.keywords, modifier = Modifier.fillMaxWidth(), ) @@ -143,9 +187,9 @@ private fun KeywordGraphLayout( ) { Column( modifier = modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), ) { - keywordList.forEach { + keywordList.take(3).forEach { with(it) { ProfileKeywordGraphItem( keyword = name, @@ -192,6 +236,9 @@ private fun ProfileKeywordSectionPreview() { nickname = "안두콩", keywordList = KeywordListModel.FakeList3, modifier = Modifier.fillMaxSize(), + isMyProfile = true, + showInfoModal = false, + onInfoClick = {}, onRefreshClick = {}, ) }