diff --git a/app/src/main/java/com/poti/android/core/designsystem/component/display/PotiDivider.kt b/app/src/main/java/com/poti/android/core/designsystem/component/display/PotiDivider.kt index 06b3a35f..cf75d119 100644 --- a/app/src/main/java/com/poti/android/core/designsystem/component/display/PotiDivider.kt +++ b/app/src/main/java/com/poti/android/core/designsystem/component/display/PotiDivider.kt @@ -6,8 +6,10 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.shape.CircleShape import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp @@ -30,10 +32,13 @@ fun PotiDivider( styleType: PotiDividerStyle, modifier: Modifier = Modifier, ) { + val shapeModifier = if (styleType == PotiDividerStyle.SMALL) Modifier.clip(CircleShape) else Modifier + Box( modifier = modifier .fillMaxWidth() .height(styleType.height) + .then(shapeModifier) .background(styleType.color), ) } diff --git a/app/src/main/java/com/poti/android/core/designsystem/component/display/PotiRating.kt b/app/src/main/java/com/poti/android/core/designsystem/component/display/PotiRating.kt index 1a998dff..9281f360 100644 --- a/app/src/main/java/com/poti/android/core/designsystem/component/display/PotiRating.kt +++ b/app/src/main/java/com/poti/android/core/designsystem/component/display/PotiRating.kt @@ -8,18 +8,20 @@ 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.graphics.vector.ImageVector import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.poti.android.R -import com.poti.android.core.designsystem.theme.Gray800 import com.poti.android.core.designsystem.theme.PotiTheme @Composable fun PotiRating( rating: String, modifier: Modifier = Modifier, + iconTint: Color = PotiTheme.colors.gray800, + textColor: Color = PotiTheme.colors.gray800, ) { Row( modifier = modifier, @@ -30,12 +32,12 @@ fun PotiRating( imageVector = ImageVector.vectorResource(id = R.drawable.ic_star), contentDescription = null, modifier = Modifier.size(21.dp), - tint = Gray800, + tint = iconTint, ) Text( text = rating, style = PotiTheme.typography.body14m, - color = Gray800, + color = textColor, ) } } diff --git a/app/src/main/java/com/poti/android/domain/model/user/UserProfile.kt b/app/src/main/java/com/poti/android/domain/model/user/UserProfile.kt new file mode 100644 index 00000000..8557decc --- /dev/null +++ b/app/src/main/java/com/poti/android/domain/model/user/UserProfile.kt @@ -0,0 +1,19 @@ +package com.poti.android.domain.model.user + +data class UserProfile( + val userId: Long, + val email: String, + val nickname: String, + val profileImageUrl: String, + val ratingAvg: Double, + val activityMessage: String, + val joinedAt: String, + val hasFavoriteArtist: Boolean, + val recruitSummary: HistorySummary, +) + +data class HistorySummary( + val total: Int, + val inProgress: Int, + val completed: Int, +) diff --git a/app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt b/app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt index 3678b04e..6aae53d1 100644 --- a/app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt +++ b/app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt @@ -43,6 +43,7 @@ fun PotiNavHost( ) profileNavGraph( paddingValues = paddingValues, + onPopBackStack = navigator.navController::popBackStack, ) } } diff --git a/app/src/main/java/com/poti/android/presentation/user/component/BadgeButton.kt b/app/src/main/java/com/poti/android/presentation/user/component/BadgeButton.kt new file mode 100644 index 00000000..61c43c93 --- /dev/null +++ b/app/src/main/java/com/poti/android/presentation/user/component/BadgeButton.kt @@ -0,0 +1,78 @@ +package com.poti.android.presentation.user.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.poti.android.R +import com.poti.android.core.common.extension.noRippleClickable +import com.poti.android.core.designsystem.theme.PotiTheme + +@Composable +fun BadgeButton( + bias: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .heightIn(min = 40.dp) + .clip(CircleShape) + .background(PotiTheme.colors.poti200) + .noRippleClickable(onClick) + .padding(vertical = 8.dp) + .padding(start = 16.dp, end = 8.dp), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = bias, + color = PotiTheme.colors.poti800, + style = PotiTheme.typography.body14m, + ) + + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_arrow_right_sm), + contentDescription = null, + modifier = Modifier.size(20.dp), + tint = PotiTheme.colors.poti800, + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun BadgeButtonPreview() { + PotiTheme { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + BadgeButton( + bias = "나의 최애 선택하기", + onClick = {}, + modifier = Modifier, + ) + + BadgeButton( + bias = "아이브", + onClick = {}, + modifier = Modifier, + ) + } + } +} diff --git a/app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt b/app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt new file mode 100644 index 00000000..3f6091d5 --- /dev/null +++ b/app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt @@ -0,0 +1,173 @@ +package com.poti.android.presentation.user.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.collectIsPressedAsState +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.material3.VerticalDivider +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.poti.android.R +import com.poti.android.core.common.extension.noRippleClickable +import com.poti.android.core.designsystem.theme.PotiTheme +import com.poti.android.domain.model.user.HistorySummary + +enum class HistorySummaryType { + ALL, + IN_PROGRESS, + FINISHED, +} + +@Composable +fun HistorySummaryCard( + title: String, + summary: HistorySummary, + onItemClick: (HistorySummaryType) -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.Start, + ) { + Text( + text = title, + color = PotiTheme.colors.black, + style = PotiTheme.typography.body16sb, + ) + + Spacer(Modifier.height(12.dp)) + + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(PotiTheme.colors.gray100) + .padding(8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + HistoryItem( + title = stringResource(R.string.user_history_all), + count = summary.total, + onClick = { onItemClick(HistorySummaryType.ALL) }, + modifier = Modifier.weight(1f), + ) + + VerticalDivider( + modifier = Modifier + .height(56.dp) + .clip(CircleShape), + thickness = 1.dp, + color = PotiTheme.colors.gray300, + ) + + HistoryItem( + title = stringResource(R.string.user_history_ongoing), + count = summary.inProgress, + onClick = { onItemClick(HistorySummaryType.IN_PROGRESS) }, + modifier = Modifier.weight(1f), + ) + + VerticalDivider( + modifier = Modifier + .height(56.dp) + .clip(CircleShape), + thickness = 1.dp, + color = PotiTheme.colors.gray300, + ) + + HistoryItem( + title = stringResource(R.string.user_history_ended), + count = summary.completed, + onClick = { onItemClick(HistorySummaryType.FINISHED) }, + modifier = Modifier.weight(1f), + ) + } + } +} + +@Composable +private fun HistoryItem( + title: String, + count: Int, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + val interactionSource = remember { MutableInteractionSource() } + val isPressed by interactionSource.collectIsPressedAsState() + val backgroundColor = if (isPressed) PotiTheme.colors.gray300 else PotiTheme.colors.gray100 + + Column( + modifier = modifier + .widthIn(92.dp) + .clip(RoundedCornerShape(12.dp)) + .background(backgroundColor) + .noRippleClickable( + interactionSource = interactionSource, + onClick = onClick, + ) + .padding(vertical = 18.5.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + text = count.toString(), + color = PotiTheme.colors.poti600, + style = PotiTheme.typography.title18sb, + ) + Text( + text = title, + color = PotiTheme.colors.gray800, + style = PotiTheme.typography.caption12m, + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun HistorySummaryCardPreview() { + val summary = HistorySummary( + total = 7, + inProgress = 2, + completed = 5, + ) + + PotiTheme { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + HistorySummaryCard( + title = "참여 내역", + summary = summary, + onItemClick = {}, + modifier = Modifier.width(328.dp), + ) + + HistorySummaryCard( + title = "참여 내역", + summary = summary, + onItemClick = {}, + ) + } + } +} diff --git a/app/src/main/java/com/poti/android/presentation/user/component/RatingBadge.kt b/app/src/main/java/com/poti/android/presentation/user/component/RatingBadge.kt new file mode 100644 index 00000000..7de356a8 --- /dev/null +++ b/app/src/main/java/com/poti/android/presentation/user/component/RatingBadge.kt @@ -0,0 +1,58 @@ +package com.poti.android.presentation.user.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.CircleShape +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.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.poti.android.core.designsystem.component.display.PotiRating +import com.poti.android.core.designsystem.theme.PotiTheme + +@Composable +fun RatingBadge( + rating: String, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .heightIn(min = 40.dp) + .widthIn(min = 71.dp) + .clip(CircleShape) + .background(PotiTheme.colors.black) + .padding(horizontal = 12.dp, vertical = 8.dp), + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.CenterVertically, + ) { + PotiRating( + rating = rating, + iconTint = PotiTheme.colors.poti200, + textColor = PotiTheme.colors.poti200, + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun RatingBadgePreview() { + PotiTheme { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + RatingBadge( + rating = "4.8", + modifier = Modifier.width(71.dp), + ) + } + } +} diff --git a/app/src/main/java/com/poti/android/presentation/user/component/UserInfo.kt b/app/src/main/java/com/poti/android/presentation/user/component/UserInfo.kt new file mode 100644 index 00000000..81a8762c --- /dev/null +++ b/app/src/main/java/com/poti/android/presentation/user/component/UserInfo.kt @@ -0,0 +1,101 @@ +package com.poti.android.presentation.user.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.poti.android.R +import com.poti.android.core.common.util.screenWidthDp +import com.poti.android.core.designsystem.theme.PotiTheme +import java.time.LocalDate +import java.time.format.DateTimeFormatter + +@Composable +fun UserInfo( + activityMessage: String, + joinedAt: String, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .clip(RoundedCornerShape(12.dp)) + .background(PotiTheme.colors.gray100) + .padding(all = screenWidthDp(12.dp)), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + UserInfoItem( + infoContent = activityMessage, + modifier = Modifier.fillMaxWidth(), + ) + UserInfoItem( + infoContent = formatJoinedDate(joinedAt), + modifier = Modifier.fillMaxWidth(), + ) + } +} + +@Composable +private fun UserInfoItem( + infoContent: String, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier, + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_bullet), + contentDescription = null, + tint = PotiTheme.colors.gray800, + ) + + Text( + text = infoContent, + color = PotiTheme.colors.black, + style = PotiTheme.typography.caption12m, + ) + } +} + +private fun formatJoinedDate(joinedAt: String): String { + return try { + val date = LocalDate.parse(joinedAt) + val formatter = DateTimeFormatter.ofPattern("yyyy년 M월 d일") + "${date.format(formatter)} 가입" + } catch (e: Exception) { + joinedAt + } +} + +@Preview(showBackground = true) +@Composable +private fun UserInfoCardPreview() { + PotiTheme { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + UserInfo( + activityMessage = "최근 3일 이내 활동", + joinedAt = "2025-12-28", + modifier = Modifier.width(328.dp), + ) + } + } +} diff --git a/app/src/main/java/com/poti/android/presentation/user/component/UserProfile.kt b/app/src/main/java/com/poti/android/presentation/user/component/UserProfile.kt new file mode 100644 index 00000000..d787b081 --- /dev/null +++ b/app/src/main/java/com/poti/android/presentation/user/component/UserProfile.kt @@ -0,0 +1,84 @@ +package com.poti.android.presentation.user.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import coil.request.ImageRequest +import com.poti.android.core.common.util.screenWidthDp +import com.poti.android.core.designsystem.theme.PotiTheme + +@Composable +fun UserProfile( + imageUrl: String, + nickname: String, + email: String, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + AsyncImage( + model = ImageRequest.Builder(LocalContext.current) + .data(imageUrl) + .crossfade(true) + .build(), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier + .size(screenWidthDp(98.dp)) + .clip(CircleShape) + .background(PotiTheme.colors.gray100), + ) + + Spacer(Modifier.height(12.dp)) + + Text( + text = nickname, + color = PotiTheme.colors.black, + style = PotiTheme.typography.body16sb, + ) + + Spacer(Modifier.height(2.dp)) + + Text( + text = email, + color = PotiTheme.colors.gray700, + style = PotiTheme.typography.caption12m, + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun UserProfilePreview() { + PotiTheme { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + UserProfile( + imageUrl = "", + nickname = "포티포티포티", + email = "poti@app.jam", + modifier = Modifier, + ) + } + } +} diff --git a/app/src/main/java/com/poti/android/presentation/user/profile/ProfileScreen.kt b/app/src/main/java/com/poti/android/presentation/user/profile/ProfileScreen.kt index 4702f339..1a35eb07 100644 --- a/app/src/main/java/com/poti/android/presentation/user/profile/ProfileScreen.kt +++ b/app/src/main/java/com/poti/android/presentation/user/profile/ProfileScreen.kt @@ -1 +1,133 @@ package com.poti.android.presentation.user.profile + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.poti.android.R +import com.poti.android.core.common.extension.onSuccess +import com.poti.android.core.common.util.HandleSideEffects +import com.poti.android.core.common.util.screenHeightDp +import com.poti.android.core.common.util.screenWidthDp +import com.poti.android.core.designsystem.component.navigation.PotiHeaderPage +import com.poti.android.core.designsystem.theme.PotiTheme +import com.poti.android.domain.model.user.HistorySummary +import com.poti.android.domain.model.user.UserProfile +import com.poti.android.presentation.user.component.HistorySummaryCard +import com.poti.android.presentation.user.component.RatingBadge +import com.poti.android.presentation.user.component.UserInfo +import com.poti.android.presentation.user.component.UserProfile +import com.poti.android.presentation.user.profile.model.ProfileUiEffect + +@Composable +fun ProfileScreenRoute( + onPopBackStack: () -> Unit, + modifier: Modifier = Modifier, + viewModel: ProfileViewModel = hiltViewModel(), +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + HandleSideEffects(viewModel.sideEffect) { effect -> + when (effect) { + ProfileUiEffect.NavigateBack -> onPopBackStack() + } + } + + uiState.userProfileLoadState.onSuccess { userProfile -> + ProfileScreen( + userProfile = userProfile, + onBackClick = onPopBackStack, + modifier = modifier, + ) + } +} + +@Composable +private fun ProfileScreen( + userProfile: UserProfile, + onBackClick: () -> Unit, + modifier: Modifier = Modifier, +) { + val scrollState = rememberScrollState() + + Column( + modifier = modifier.fillMaxSize(), + ) { + PotiHeaderPage( + onNavigationClick = onBackClick, + ) + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(scrollState) + .padding( + horizontal = screenWidthDp(16.dp), + vertical = screenHeightDp(20.dp), + ), + verticalArrangement = Arrangement.spacedBy(24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + UserProfile( + imageUrl = userProfile.profileImageUrl, + nickname = userProfile.nickname, + email = userProfile.email, + ) + + RatingBadge( + rating = userProfile.ratingAvg.toString(), + ) + + UserInfo( + activityMessage = userProfile.activityMessage, + joinedAt = userProfile.joinedAt, + modifier = Modifier.fillMaxWidth(), + ) + + HistorySummaryCard( + title = stringResource(R.string.user_history_recruit), + summary = userProfile.recruitSummary, + onItemClick = { type -> }, + modifier = Modifier.fillMaxWidth(), + ) + } + } +} + +@Preview(showBackground = true) +@Composable +private fun ProfileScreenPreview() { + PotiTheme { + ProfileScreen( + userProfile = UserProfile( + userId = 1L, + email = "akkma@app.jam", + nickname = "분철의 악마", + profileImageUrl = "", + ratingAvg = 4.8, + activityMessage = "최근 3일 이내 활동", + joinedAt = "2025-12-28", + hasFavoriteArtist = true, + recruitSummary = HistorySummary( + total = 7, + inProgress = 2, + completed = 5, + ), + ), + onBackClick = { }, + modifier = Modifier, + ) + } +} diff --git a/app/src/main/java/com/poti/android/presentation/user/profile/ProfileViewModel.kt b/app/src/main/java/com/poti/android/presentation/user/profile/ProfileViewModel.kt index 5fba4db5..c210de81 100644 --- a/app/src/main/java/com/poti/android/presentation/user/profile/ProfileViewModel.kt +++ b/app/src/main/java/com/poti/android/presentation/user/profile/ProfileViewModel.kt @@ -1,9 +1,50 @@ package com.poti.android.presentation.user.profile -import androidx.lifecycle.ViewModel +import com.poti.android.core.base.BaseViewModel +import com.poti.android.core.common.state.ApiState +import com.poti.android.domain.model.user.HistorySummary +import com.poti.android.domain.model.user.UserProfile +import com.poti.android.presentation.user.profile.model.ProfileUiEffect +import com.poti.android.presentation.user.profile.model.ProfileUiIntent +import com.poti.android.presentation.user.profile.model.ProfileUiState import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @HiltViewModel -class ProfileViewModel @Inject constructor() : ViewModel() { +class ProfileViewModel @Inject constructor() : BaseViewModel( + initialState = ProfileUiState(), +) { + override fun processIntent(intent: ProfileUiIntent) { + when (intent) { + ProfileUiIntent.OnBackClick -> sendEffect(ProfileUiEffect.NavigateBack) + } + } + + init { + loadUserProfile() + } + + private fun loadUserProfile() { + updateState { + copy( + userProfileLoadState = ApiState.Success( + UserProfile( + userId = 1L, + email = "akkma@app.jam", + nickname = "분철의 악마", + profileImageUrl = "", + ratingAvg = 4.8, + activityMessage = "최근 3일 이내 활동", + joinedAt = "2025-12-28", + hasFavoriteArtist = true, + recruitSummary = HistorySummary( + total = 7, + inProgress = 2, + completed = 5, + ), + ), + ), + ) + } + } } diff --git a/app/src/main/java/com/poti/android/presentation/user/profile/model/Contracts.kt b/app/src/main/java/com/poti/android/presentation/user/profile/model/Contracts.kt new file mode 100644 index 00000000..1825e38f --- /dev/null +++ b/app/src/main/java/com/poti/android/presentation/user/profile/model/Contracts.kt @@ -0,0 +1,19 @@ +package com.poti.android.presentation.user.profile.model + +import com.poti.android.core.base.UiEffect +import com.poti.android.core.base.UiIntent +import com.poti.android.core.base.UiState +import com.poti.android.core.common.state.ApiState +import com.poti.android.domain.model.user.UserProfile + +data class ProfileUiState( + val userProfileLoadState: ApiState = ApiState.Loading, +) : UiState + +sealed interface ProfileUiIntent : UiIntent { + data object OnBackClick : ProfileUiIntent +} + +sealed interface ProfileUiEffect : UiEffect { + data object NavigateBack : ProfileUiEffect +} diff --git a/app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt b/app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt index e3ec1495..ca28a0a9 100644 --- a/app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt +++ b/app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt @@ -1,10 +1,13 @@ package com.poti.android.presentation.user.profile.navigation import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding +import androidx.compose.ui.Modifier import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import com.poti.android.core.navigation.Route +import com.poti.android.presentation.user.profile.ProfileScreenRoute import kotlinx.serialization.Serializable sealed interface ProfileRoute : Route { @@ -18,6 +21,12 @@ fun NavController.navigateToProfile(userId: Long) { fun NavGraphBuilder.profileNavGraph( paddingValues: PaddingValues, + onPopBackStack: () -> Unit, ) { - composable { } + composable { + ProfileScreenRoute( + onPopBackStack = onPopBackStack, + modifier = Modifier.padding(paddingValues), + ) + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 66f3960a..f4f1a65c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -79,4 +79,11 @@ 총 입금 금액 %s원 + + 모집 내역 + 참여 내역 + 전체 + 진행중 + 종료 +