diff --git a/app/src/main/java/com/paw/key/core/designsystem/theme/Type.kt b/app/src/main/java/com/paw/key/core/designsystem/theme/Type.kt index 027b9c64..e5366a2a 100644 --- a/app/src/main/java/com/paw/key/core/designsystem/theme/Type.kt +++ b/app/src/main/java/com/paw/key/core/designsystem/theme/Type.kt @@ -420,7 +420,7 @@ fun pawKeyTypography(): PawKeyTypography { ), mainButtonActive = pawKeyTextStyle( fontFamily = PretendardRegular, - fontWeight = FontWeight.Normal, + fontWeight = FontWeight.SemiBold, fontSize = 18.sp, lineHeight = 16.sp, letterSpacing = 0.em diff --git a/app/src/main/java/com/paw/key/data/dto/response/petprofile/PetProfileResponseDto.kt b/app/src/main/java/com/paw/key/data/dto/response/petprofile/PetProfileResponseDto.kt index 4042c048..bcb74c27 100644 --- a/app/src/main/java/com/paw/key/data/dto/response/petprofile/PetProfileResponseDto.kt +++ b/app/src/main/java/com/paw/key/data/dto/response/petprofile/PetProfileResponseDto.kt @@ -1,5 +1,6 @@ package com.paw.key.data.dto.response.petprofile +import androidx.core.net.toUri import com.paw.key.domain.model.entity.petprofile.PetProfileEntity import com.paw.key.domain.model.entity.petprofile.TraitEntity import kotlinx.serialization.SerialName @@ -35,7 +36,7 @@ data class PetProfileResponseDto( val traits: List, @SerialName("walkCount") - val walkCount: Int + val walkCount: Int, ) { fun toEntity() = PetProfileEntity( petId = petId, @@ -45,7 +46,7 @@ data class PetProfileResponseDto( age = age, isAgeKnown = isAgeKnown, breed = breed, - imageUrl = imageUrl, + imageUrl = imageUrl.toUri(), walkCount = walkCount, traits = traits.map { it.toEntity() } ) @@ -57,9 +58,8 @@ data class TraitDto( val category: String, @SerialName("option") - val option: String -) -{ + val option: String, +) { fun toEntity() = TraitEntity( category = category, option = option diff --git a/app/src/main/java/com/paw/key/domain/model/entity/petprofile/PetProfileEntity.kt b/app/src/main/java/com/paw/key/domain/model/entity/petprofile/PetProfileEntity.kt index 015634ba..e151587d 100644 --- a/app/src/main/java/com/paw/key/domain/model/entity/petprofile/PetProfileEntity.kt +++ b/app/src/main/java/com/paw/key/domain/model/entity/petprofile/PetProfileEntity.kt @@ -1,5 +1,7 @@ package com.paw.key.domain.model.entity.petprofile +import android.net.Uri + data class PetProfileEntity( val petId: Long, val name: String, @@ -8,7 +10,7 @@ data class PetProfileEntity( val age: Int, val isAgeKnown: Boolean, val breed: String, - val imageUrl: String, + val imageUrl: Uri, val traits: List, val walkCount: Int ) diff --git a/app/src/main/java/com/paw/key/presentation/ui/main/MainNavigator.kt b/app/src/main/java/com/paw/key/presentation/ui/main/MainNavigator.kt index 09fdbe64..7fae9b40 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/main/MainNavigator.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/main/MainNavigator.kt @@ -21,13 +21,12 @@ import com.paw.key.presentation.ui.dummy.next.navigateDummyNext import com.paw.key.presentation.ui.home.navigation.navigateHome import com.paw.key.presentation.ui.home.navigation.navigateHomeLocationSetting import com.paw.key.presentation.ui.login.navigation.navigateLogin -import com.paw.key.presentation.ui.mypage.navigation.navigateArchivedCourse -import com.paw.key.presentation.ui.mypage.navigation.navigateArchivedDetail -import com.paw.key.presentation.ui.mypage.navigation.navigateMyPage -import com.paw.key.presentation.ui.mypage.navigation.navigatePetProfile -import com.paw.key.presentation.ui.mypage.navigation.navigateSavedCourse -import com.paw.key.presentation.ui.mypage.navigation.navigateSavedDetail -import com.paw.key.presentation.ui.mypage.navigation.navigateUserProfile +import com.paw.key.presentation.ui.mypage.courseinfo.model.CourseType +import com.paw.key.presentation.ui.mypage.courseinfo.navigation.navigateCourseInfo +import com.paw.key.presentation.ui.mypage.main.navigation.navigateMyPage +import com.paw.key.presentation.ui.mypage.petinfo.navigation.navigatePetProfile +import com.paw.key.presentation.ui.mypage.petinfo.navigation.navigatePetProfileList +import com.paw.key.presentation.ui.mypage.userinfo.navigation.navigateUserProfile import com.paw.key.presentation.ui.onboard.navigation.navigateOnboarding import com.paw.key.presentation.ui.region.navigation.navigateRegional import com.paw.key.presentation.ui.signup.navigation.navigateSignUp @@ -96,32 +95,24 @@ class MainNavigator( navController.navigatePetProfile(navOptions = navOptions) } - fun navigateSavedCourse(navOptions: NavOptions? = null) { - navController.navigateSavedCourse(navOptions = navOptions) + fun navigatePetProfileList(navOptions: NavOptions? = null) { + navController.navigatePetProfileList(navOptions = navOptions) } - - fun navigateSavedDetail( - pageId: Int, - routeId: Int, - navOptions: NavOptions? = null) - { - navController.navigateSavedDetail( - pageId = pageId, - routeId = routeId, - navOptions = navOptions - ) + + fun navigateCourseInfo( + courseType: CourseType, + navOptions: NavOptions? = null, + ) { + navController.navigateCourseInfo(courseType = courseType, navOptions = navOptions) } // Todo : 나중에 로직 플로우 확인하고 수정예정 fun navigateSignUp(navOptions: NavOptions? = null) { navController.navigateSignUp(navOptions) } - - fun navigateArchivedCourse(navOptions: NavOptions? = null) { - navController.navigateArchivedCourse(navOptions = navOptions) - } - fun navigateCourse(index : Int = 0, navOptions: NavOptions? = null) { + + fun navigateCourse(index: Int = 0, navOptions: NavOptions? = null) { navController.navigateCourse( index = index, navOptions = navOptions @@ -142,9 +133,9 @@ class MainNavigator( // } fun navigateSharedWalkCourse( routeId: Int, - pageId : Int, - navOptions: NavOptions? = null) - { + pageId: Int, + navOptions: NavOptions? = null, + ) { navController.navigateSharedWalkCourse( routeId = routeId, pageId = pageId, @@ -155,7 +146,7 @@ class MainNavigator( fun navigateSharedWalkReview( pageId: Int, routeId: Int, - navOptions: NavOptions? = null + navOptions: NavOptions? = null, ) { navController.navigateSharedWalkReview( pageId = pageId, @@ -167,7 +158,7 @@ class MainNavigator( fun navigateSharedWalkCompletion( pageId: Int, routeId: Int, - navOptions: NavOptions? = null + navOptions: NavOptions? = null, ) { navController.navigateSharedWalkCompletion( pageId = pageId, @@ -176,23 +167,11 @@ class MainNavigator( ) } - fun navigateArchivedDetail( - routeId: Int, - pageId : Int, - navOptions: NavOptions? = null - ) { - navController.navigateArchivedDetail( - pageId = pageId, - routeId = routeId, - navOptions = navOptions - ) - } - fun navigateWalkCourse(navOptions: NavOptions? = null) { navController.navigateWalkCourse(navOptions = navOptions) } - fun navigateWalkCompletion(routeId : Int, navOptions: NavOptions? = null) { + fun navigateWalkCompletion(routeId: Int, navOptions: NavOptions? = null) { navController.navigateWalkCompletion( routeId = routeId, navOptions = navOptions @@ -219,7 +198,6 @@ class MainNavigator( } - @Composable fun showBottomBar() = MainTab.contains { currentDestination?.hasRoute(it::class) == true diff --git a/app/src/main/java/com/paw/key/presentation/ui/main/MainTab.kt b/app/src/main/java/com/paw/key/presentation/ui/main/MainTab.kt index cff8dab9..69bef687 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/main/MainTab.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/main/MainTab.kt @@ -4,16 +4,16 @@ import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.compose.runtime.Composable import com.paw.key.R -import com.paw.key.core.navigation.MainTabRoute -import com.paw.key.presentation.ui.home.navigation.Home -import com.paw.key.presentation.ui.course.entire.navigation.Course -import com.paw.key.presentation.ui.community.navigation.Community -import com.paw.key.presentation.ui.mypage.navigation.MyPage -import com.paw.key.R.string.ic_home_description -import com.paw.key.R.string.ic_course_description import com.paw.key.R.string.ic_community_description +import com.paw.key.R.string.ic_course_description +import com.paw.key.R.string.ic_home_description import com.paw.key.R.string.ic_mypage_description +import com.paw.key.core.navigation.MainTabRoute import com.paw.key.core.navigation.Route +import com.paw.key.presentation.ui.community.navigation.Community +import com.paw.key.presentation.ui.course.entire.navigation.Course +import com.paw.key.presentation.ui.home.navigation.Home +import com.paw.key.presentation.ui.mypage.main.navigation.MyPage enum class MainTab( diff --git a/app/src/main/java/com/paw/key/presentation/ui/main/PawKeyNavHost.kt b/app/src/main/java/com/paw/key/presentation/ui/main/PawKeyNavHost.kt index efd65292..3f4fd6c4 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/main/PawKeyNavHost.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/main/PawKeyNavHost.kt @@ -25,13 +25,11 @@ import com.paw.key.presentation.ui.dummy.next.dummyNextNavGraph import com.paw.key.presentation.ui.home.navigation.homeLocationSettingNavGraph import com.paw.key.presentation.ui.home.navigation.homeNavGraph import com.paw.key.presentation.ui.login.navigation.loginNavGraph -import com.paw.key.presentation.ui.mypage.navigation.archivedCourseNavGraph -import com.paw.key.presentation.ui.mypage.navigation.archivedDetailNavGraph -import com.paw.key.presentation.ui.mypage.navigation.myPageNavGraph -import com.paw.key.presentation.ui.mypage.navigation.petProfileNavGraph -import com.paw.key.presentation.ui.mypage.navigation.savedCourseNavGraph -import com.paw.key.presentation.ui.mypage.navigation.savedDetailNavGraph -import com.paw.key.presentation.ui.mypage.navigation.userProfileNavGraph +import com.paw.key.presentation.ui.mypage.courseinfo.navigation.courseInfoNavGraph +import com.paw.key.presentation.ui.mypage.main.navigation.myPageNavGraph +import com.paw.key.presentation.ui.mypage.petinfo.navigation.petProfileListNavGraph +import com.paw.key.presentation.ui.mypage.petinfo.navigation.petProfileNavGraph +import com.paw.key.presentation.ui.mypage.userinfo.navigation.userProfileNavGraph import com.paw.key.presentation.ui.onboard.navigation.onboardingNavGraph import com.paw.key.presentation.ui.region.navigation.regionalNavGraph import com.paw.key.presentation.ui.signup.navigation.signUpNavGraph @@ -100,10 +98,7 @@ fun PawKeyNavHost( navigateNext = navigator::navigateWalkCourse, navigateToDetail = { postId, routeId -> // Todo : 마찬가지로 이것도 그냥 넣어놓음 나중에 리스트 연결 후 예쩡 / 리스트 아이템이동 - navigator.navigateArchivedDetail( - pageId = postId, - routeId = routeId - ) + }, setOnVisibleRecord = navigator::setOnVisibleRecord, snackBarHostState = snackbarHostState @@ -169,10 +164,7 @@ fun PawKeyNavHost( navigateUp = navigator::navigateUp, navigateNext = navigator::navigateCourse, navigateShared = { routeId, pageId -> - navigator.navigateArchivedDetail( - pageId = pageId, - routeId = routeId - ) + }, snackBarHostState = snackbarHostState ) @@ -187,54 +179,16 @@ fun PawKeyNavHost( myPageNavGraph( paddingValues = paddingValues, navigateUp = navigator::navigateUp, - navigateUserProfile = navigator::navigateUserProfile, navigatePetProfile = navigator::navigatePetProfile, - navigateArchivedCourse = navigator::navigateArchivedCourse, - navigateSavedCourse = navigator::navigateSavedCourse, - snackBarHostState = snackbarHostState - ) - - savedCourseNavGraph( - paddingValues = paddingValues, - navigateUp = navigator::navigateUp, - navigateNext = { routeId, pageId -> - navigator.navigateSavedDetail( - pageId = pageId, - routeId = routeId - ) - }, - snackBarHostState = snackbarHostState - ) - - archivedCourseNavGraph( - navigateUp = navigator::navigateUp, - navigateNext = { routeId, pageId -> - navigator.navigateArchivedDetail( - pageId = pageId, - routeId = routeId - ) + navigateCourseInfo = { courseType -> + navigator.navigateCourseInfo(courseType) }, - modifier = modifier - ) - - savedDetailNavGraph( - navigateUp = navigator::navigateUp, - navigateToWalk = navigator::navigateWalkCourse, - snackBarHostState = snackbarHostState + navigatePetProfileList = navigator::navigatePetProfileList, + navigateUserProfile = navigator::navigateUserProfile, ) - archivedDetailNavGraph( - // Todo 그냥 리스트에서 상세보기 후 뒤로가기 + courseInfoNavGraph( navigateUp = navigator::navigateUp, - /*navigateDetail = { - navigator.navController.navigateCourse(index = 1, navOptions = null) - },*/ - navigateToSharedWalk = { routeId, pageId -> - navigator.navigateSharedWalkCourse( - routeId = routeId, - pageId = pageId - ) - }, modifier = modifier ) @@ -250,6 +204,11 @@ fun PawKeyNavHost( modifier = modifier ) + petProfileListNavGraph( + navigateUp = navigator::navigateUp, + navigatePetProfile = navigator::navigatePetProfile, + ) + dummyNavGraph( paddingValues = paddingValues, navigateUp = navigator::navigateUp, diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/ArchivedCourseDetailScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/ArchivedCourseDetailScreen.kt deleted file mode 100644 index d39e8a16..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/ArchivedCourseDetailScreen.kt +++ /dev/null @@ -1,218 +0,0 @@ -package com.paw.key.presentation.ui.mypage - -import android.app.Activity -import android.util.Log -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.LazyColumn -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalView -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.zIndex -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.paw.key.R -import com.paw.key.core.designsystem.component.CourseDetail -import com.paw.key.core.designsystem.component.ImageModal -import com.paw.key.core.designsystem.component.PawkeyButton -import com.paw.key.core.designsystem.component.TopBar -import com.paw.key.core.designsystem.theme.PawKeyTheme -import com.paw.key.core.designsystem.theme.White1 -import com.paw.key.core.util.PreferenceDataStore -import com.paw.key.domain.model.entity.walklist.CategoryTop3Entity -import com.paw.key.presentation.ui.mypage.viewmodel.ArchivedDetailViewModel -import kotlinx.coroutines.flow.first - -@Composable -fun ArchivedDetailRoute( - navigateUp: () -> Unit, - navigateToSharedWalk: (Int, Int) -> Unit, - routeId : Int, - pageId : Int, - modifier: Modifier = Modifier, - viewModel: ArchivedDetailViewModel = hiltViewModel(), -) { - val state by viewModel.state.collectAsStateWithLifecycle() - val userId = PreferenceDataStore.getUserId() - - LaunchedEffect(Unit) { - Log.e("ArchivedDetailRoute", "userId: ${userId.first()}, pageId: $pageId") - viewModel.getWalkDetail(userId.first(), pageId) - viewModel.getWalkTopPopular(userId.first(), routeId) - } - - ArchivedCourseDetailScreen( - title = state.postTitle, - petName = state.petName, - date = state.createdAt, - location = state.regionName, - isLike = state.isLiked, - content = state.postContent, - petProfileImage = state.petProfileImage, - routeMapImageUrl = state.routeMapImageUrl, - categorySummary = state.categorySummary, - categoryTop3 = state.categoryTop3, - totalReviewCount = state.totalReviewCount, - walkingImageUrls = state.walkingImageUrls, - - clickImage = state.clickImage, - - onClickImage = { - viewModel.onClickImage(it) - }, - - navigateUp = navigateUp, - navigateToSharedWalk = { - Log.e("TAG", "ArchivedDetailRoute: $routeId, $pageId", ) - navigateToSharedWalk(routeId, pageId) - }, - modifier = modifier - ) -} - -@Composable -fun ArchivedCourseDetailScreen( - title: String, - petName: String, - date: String, - location: String, - isLike: Boolean, - content: String, - petProfileImage: String, - routeMapImageUrl: String, - categorySummary: List, - categoryTop3: List, - totalReviewCount: Int, - walkingImageUrls: List, - clickImage: String, - navigateUp: () -> Unit, - navigateToSharedWalk: () -> Unit, - onClickImage: (String) -> Unit, - modifier: Modifier = Modifier, -) { - var isImageExpanded by remember { mutableStateOf(false) } - val isLiked = remember { mutableStateOf(false) } - val view = LocalView.current - val statusBarColor = PawKeyTheme.colors.green500 - - val context = LocalContext.current - val window = (context as? Activity)?.window - val previousNavBarColor = remember { window?.navigationBarColor } - - DisposableEffect(Unit) { - window?.navigationBarColor = statusBarColor.toArgb() - - onDispose { - // 화면에서 벗어날 때 원래 색으로 복원 - previousNavBarColor?.let { - window?.navigationBarColor = it - } - } - } - - Column( - modifier = modifier - .fillMaxSize() - .background(color = White1) - ) { - TopBar( - title = "내가 기록한 산책 루트", - onBackClick = navigateUp - ) - - LazyColumn( - modifier = modifier - .fillMaxSize() - .background(color = Color.Transparent) - .zIndex(2F) - ) { - item { - CourseDetail( - title = title, - petName = petName, - date = date, - Icon = if(isLiked.value) R.drawable.ic_eye_linear_gray_invalid else R.drawable.ic_eye_linear_gray_valid, - location = location, - content = content, - onClickLike = {}, - petProfileImage = petProfileImage, - routeMapImageUrl = routeMapImageUrl, - categorySummary = categorySummary, - categoryTop3 = categoryTop3, - totalReviewCount = totalReviewCount, - walkingImageUrls = walkingImageUrls, - onImageClick = { - onClickImage(it) - isImageExpanded = true - } - ) - - Spacer(modifier = Modifier.height(36.dp)) - - PawkeyButton( - text = "해당 루트로 산책하기", - enabled = true, - onClick = navigateToSharedWalk, - modifier = Modifier - .fillMaxWidth() - .padding(top = 16.dp) - .background(color = PawKeyTheme.colors.green500) - ) - } - } - } - if (isImageExpanded) { - ImageModal( - imageUrl = clickImage, - onDismiss = { isImageExpanded = false } - ) - } -} - -@Preview -@Composable -fun ArchivedCourseDetailPreview() { - PawKeyTheme { - ArchivedCourseDetailScreen( - title = "홍대 주변 좋은 산책 코스", - petName = "핑구", - date = "2025/06/30", - location = "홍대입구역", - isLike = true, - content = "산책로가 깨끗하고 벚꽃이 예뻐요!", - petProfileImage = "https://pawkey-server.com/image/profile.png", - routeMapImageUrl = "https://pawkey-server.com/image/map.png", - categoryTop3 = listOf( - CategoryTop3Entity(1, "안전", 2, "차량이 거의 다니지 않아요", 1, 42), - CategoryTop3Entity(1, "안전", 3, "킥보드가 거의 없어요", 2, 37), - CategoryTop3Entity(2, "편리성", 7, "조명이 밝아요", 3, 35) - ), - totalReviewCount = 42, - walkingImageUrls = listOf( - "https://pawkey-server.com/image/walk1.jpg", - "https://pawkey-server.com/image/walk2.jpg" - ), - categorySummary = listOf("안전", "편리성"), - clickImage = "", - onClickImage = {}, - navigateUp = {}, - navigateToSharedWalk = {}, - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/ArchivedCourseListScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/ArchivedCourseListScreen.kt deleted file mode 100644 index 5181f982..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/ArchivedCourseListScreen.kt +++ /dev/null @@ -1,104 +0,0 @@ -package com.paw.key.presentation.ui.mypage - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.paw.key.core.designsystem.component.CourseCard -import com.paw.key.core.designsystem.component.TopBar -import com.paw.key.core.designsystem.theme.PawKeyTheme -import com.paw.key.core.util.PreferenceDataStore -import com.paw.key.presentation.ui.mypage.state.ArchivedListState -import com.paw.key.presentation.ui.mypage.viewmodel.ArchivedListViewModel -import kotlinx.coroutines.flow.first - -@Composable -fun ArchivedCourseRoute( - navigateUp: () -> Unit, - navigateNext: (Int, Int) -> Unit, - modifier: Modifier = Modifier, - viewModel: ArchivedListViewModel = hiltViewModel() -) { - val state = viewModel.state.collectAsStateWithLifecycle() - val userId = PreferenceDataStore.getUserId() - LaunchedEffect(Unit) { - // Todo : userId 네비게이션 연결 - viewModel.getArchivedList(userId = userId.first()) - } - - ArchivedCourseListScreen( - state = state.value, - navigateUp = navigateUp, - navigateNext = { routeId, pageId -> - navigateNext(routeId, pageId) - }, - onClickLike = { postId, isLiked -> - viewModel.toggleLike(postId = postId, isLiked = isLiked) - }, - modifier = modifier - ) -} - -@Composable -fun ArchivedCourseListScreen( - state: ArchivedListState, - navigateUp: () -> Unit, - navigateNext: (Int, Int) -> Unit, - onClickLike: (postId: Int, isLiked: Boolean) -> Unit, - modifier: Modifier = Modifier -) { - Column { - TopBar( - title = "내가 기록한 산책 루트", - onBackClick = navigateUp - ) - - LazyColumn( - modifier = modifier - .fillMaxWidth() - .padding(16.dp) - .background(PawKeyTheme.colors.white1) - ) { - itemsIndexed(state.courseList) { _, item -> - CourseCard( - postId = item.postId.toInt(), - title = item.title, - createdAt = item.createdAt, - representativeImageUrl = item.representativeImageUrl, - petName = item.writer.first().petName, - petProfileImageUrl = item.writer.first().petProfileImageUrl, - descriptionTags = item.descriptionTags, - isLiked = null, - isPublic = item.isPublic, // 눈아이콘만 표시 - onClickItem = { - navigateNext(item.routeId.toInt(), item.postId) - }, - onClickLike = null, - isMine = false - ) - } - } - } -} - -@Preview(showBackground = true) -@Composable -fun ArchivedCourseListScreenPreview() { - PawKeyTheme { - ArchivedCourseListScreen( - state = ArchivedListState(), - navigateUp = {}, - navigateNext = { _, _ -> - }, - onClickLike = { _, _ -> - } - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/MyPageScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/MyPageScreen.kt deleted file mode 100644 index 9723e9d7..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/MyPageScreen.kt +++ /dev/null @@ -1,382 +0,0 @@ -package com.paw.key.presentation.ui.mypage - -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Icon -import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.unit.dp -import androidx.compose.ui.tooling.preview.Preview -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import coil.compose.AsyncImage -import coil.request.ImageRequest -import com.paw.key.R -import com.paw.key.core.designsystem.theme.PawKeyTheme -import com.paw.key.core.util.PreferenceDataStore -import com.paw.key.core.extension.noRippleClickable -import com.paw.key.presentation.ui.mypage.component.GrayChip -import com.paw.key.presentation.ui.mypage.state.MyPageState -import com.paw.key.presentation.ui.mypage.viewmodel.MyPageViewModel -import kotlinx.coroutines.flow.first - -@Composable -fun MyPageRoute( - paddingValues: PaddingValues, - navigateUp: () -> Unit, - navigateUserProfile: () -> Unit, - navigatePetProfile: () -> Unit, - navigateArchivedCourse: () -> Unit, - navigateSavedCourse: () -> Unit, - snackBarHostState: SnackbarHostState, - modifier: Modifier = Modifier, - viewModel: MyPageViewModel = hiltViewModel() -) { - val state = viewModel.state.collectAsStateWithLifecycle() - val userId = PreferenceDataStore.getUserId() - - LaunchedEffect(Unit) { - viewModel.getUserProfiles(userId = userId.first()) - viewModel.getPetProfiles(userId = userId.first()) - } -// LaunchedEffect(Unit) { -// val userId = 2 // ← 임시 테스트용 -// viewModel.getUserProfiles(userId = userId) -// viewModel.getPetProfiles(userId = userId) -// } - - MyPageScreen( - state = state.value, - paddingValues = paddingValues, - navigateUp = navigateUp, - navigateUserProfile = navigateUserProfile, - navigatePetProfile = navigatePetProfile, - navigateArchivedCourse = navigateArchivedCourse, - navigateSavedCourse = navigateSavedCourse, - snackBarHostState = snackBarHostState, - modifier = modifier - ) -} - -@Composable -fun MyPageScreen( - state: MyPageState, - paddingValues: PaddingValues, - navigateUp: () -> Unit, - navigateUserProfile: () -> Unit, - navigatePetProfile: () -> Unit, - navigateArchivedCourse: () -> Unit, - navigateSavedCourse : () -> Unit, - snackBarHostState: SnackbarHostState, - modifier: Modifier = Modifier -) { - Box( - modifier = modifier - .fillMaxSize() - .background(PawKeyTheme.colors.white2) - .padding(paddingValues) - ){ - LazyColumn( - modifier = modifier - .padding(paddingValues) - .fillMaxSize() - .padding(bottom = 80.dp) - ) { - item { - Text( - text = "마이페이지", - style = PawKeyTheme.typography.head22B, - modifier = Modifier.padding(start = 16.dp, top = 16.dp, bottom = 12.dp) - ) - OwnerCard( - ownerName = state.ownerName, - navigateUserProfile = navigateUserProfile - ) - Spacer(modifier = Modifier.height(19.dp)) - - PetCard( - name = state.petName, - age = state.petAge, - gender = if (state.petGender == "M") { - "남아" - } else { - "여아" - }, - tags = state.petTags, - walkCount = state.walkCount, - totalDistance = state.totalDistance, - image = state.petImageUrl, - navigatePetProfile = navigatePetProfile - ) - - Spacer(modifier = Modifier.height(12.dp)) - - WalkRouteList( - routes = listOf("저장한 산책 루트", "내가 기록한 산책 루트"), - navigateSavedCourse = navigateSavedCourse, - navigateArchivedCourse = navigateArchivedCourse - ) - } - } - } -} - -@Composable -fun OwnerCard( - ownerName: String, - navigateUserProfile: () -> Unit, - modifier: Modifier = Modifier -) { - Row( - modifier = modifier - .padding(horizontal = 16.dp, vertical = 8.dp) - .height(80.dp) - .fillMaxWidth() - .background(Color.White, RoundedCornerShape(12.dp)) - .padding(16.dp) - .noRippleClickable { navigateUserProfile() }, - - verticalAlignment = Alignment.CenterVertically - ) { - Text(text = ownerName, style = PawKeyTheme.typography.head20B2) - Spacer(Modifier.width(10.dp)) - Text(text = "견주", style = PawKeyTheme.typography.body14M, color = PawKeyTheme.colors.gray400) - - Spacer(modifier = Modifier.weight(1f)) - Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_arrow_right), - contentDescription = "견주 프로필 이동", - tint = PawKeyTheme.colors.gray300 - ) - } -} - -@Composable -fun PetCard( - name: String, - age: String, - gender: String, - tags: List, - image: String, - walkCount: Int, - totalDistance: String, - navigatePetProfile: () -> Unit, - modifier: Modifier = Modifier -) { - Column( - modifier = modifier - .padding(horizontal = 16.dp, vertical = 8.dp) - .fillMaxWidth() - .clip(RoundedCornerShape(12.dp)) - .background(Color.White) - ) { - Row( - modifier = modifier - .fillMaxWidth() - .background(PawKeyTheme.colors.green500) - .height(44.dp) - .padding(12.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Box( - modifier = Modifier - .size(20.dp), // Text의 베이스라인에 맞추기 위해 아이콘 크기 명시 - contentAlignment = Alignment.Center - ) { - Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_personal_card), - contentDescription = "반려견 프로필", - modifier = Modifier.padding(top = 4.dp), - - tint = Color.White - ) - } - Spacer(modifier.width(4.dp) - ) - Text( - text = "반려견 프로필", - style = PawKeyTheme.typography.body16Sb, - color = Color.White - ) - Spacer(modifier = Modifier.weight(1f)) - Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_arrow_right), - modifier = modifier.clickable { navigatePetProfile() }, - contentDescription = "반려견 프로필 이동", - tint = PawKeyTheme.colors.white1 - ) - } - - Column(modifier = modifier.padding(16.dp)) { - Row(verticalAlignment = Alignment.CenterVertically) { - AsyncImage( - model = ImageRequest.Builder(LocalContext.current) - .data(image) - .crossfade(true) - .build(), - contentDescription = null, - modifier = modifier - .size(64.dp) - .clip(CircleShape), - contentScale = ContentScale.Crop - ) - - Spacer(modifier.width(16.dp)) - Column { - Text(name, style = PawKeyTheme.typography.head20B2) - Text("$age · $gender", style = PawKeyTheme.typography.caption12R, color = PawKeyTheme.colors.gray300) - } - } - - Spacer(modifier.height(8.dp)) - - Row( - modifier = modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(4.dp) - ) { - tags.forEach { - GrayChip( - text = it - ) - } - } - } - - Row( - modifier = modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 12.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceEvenly - ) { - Column( - modifier = modifier.weight(1f), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text("산책 횟수", style = PawKeyTheme.typography.caption12Sb1) - Text( - text = "${walkCount}회", - style = PawKeyTheme.typography.head20Sb, - color = PawKeyTheme.colors.green500 - ) - } - - Box( - modifier = modifier - .height(32.dp) - .width(1.dp) - .background(color = PawKeyTheme.colors.gray100) - ) - - Column( - modifier = modifier.weight(1f), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text("누적 거리", style = PawKeyTheme.typography.caption12Sb1) - Text( - totalDistance, - style = PawKeyTheme.typography.head20Sb, - color = PawKeyTheme.colors.green500 - ) - } - } - } -} - -@Composable -fun WalkRouteList( - routes: List, - navigateSavedCourse: () -> Unit, - navigateArchivedCourse: () -> Unit, - modifier: Modifier = Modifier -) { - Column( - modifier = modifier - .fillMaxWidth() - .background(Color.White) - .padding(horizontal = 16.dp) - ) { - Spacer(modifier = Modifier.height(16.dp)) - Text("산책 루트 관리", style = PawKeyTheme.typography.caption12Sb1, color = Color.Gray) - Spacer(modifier = Modifier.height(8.dp)) - - routes.forEachIndexed { index, route -> - if (index != 0) { - HorizontalDivider(thickness = 1.dp, color = PawKeyTheme.colors.gray50) - } - val iconRes = if (index == 0) { - R.drawable.ic_mypage_heart - } else { - R.drawable.ic_mypage_edit - } - Row( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 20.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - imageVector = ImageVector.vectorResource(id = iconRes), - contentDescription = "산책루트 아이콘" - ) - Text( - - text = route, - modifier = Modifier - .padding(start = 8.dp) - .weight(1f), - style = PawKeyTheme.typography.body16Sb, - color = PawKeyTheme.colors.gray950 - ) - Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_arrow_right), - modifier = modifier.clickable { - if (index == 0) - navigateSavedCourse() - else navigateArchivedCourse() - }, - contentDescription = "산책루트 메뉴 이동" - ) - } - } - } -} - -@Preview(showBackground = true) -@Composable -private fun MyPageScreenPreview() { - PawKeyTheme { - MyPageScreen(state = MyPageState( - ownerName = "김도기님", - petName = "포비", - petAge = "12세", - petGender = "여아", - petTags = listOf("조금 느긋해요", "#오토바이소리", "#대형견"), - walkCount = 7, - totalDistance = "14km" - ), - paddingValues = PaddingValues(), - navigateUp = {}, - navigateUserProfile = {}, - navigatePetProfile = {}, - navigateArchivedCourse = {}, - navigateSavedCourse = {}, - snackBarHostState = SnackbarHostState() - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/PetProfileScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/PetProfileScreen.kt deleted file mode 100644 index f2235975..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/PetProfileScreen.kt +++ /dev/null @@ -1,217 +0,0 @@ -package com.paw.key.presentation.ui.mypage - -import android.R.attr.contentDescription -import androidx.compose.foundation.Image -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.compose.ui.Alignment -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import coil.compose.AsyncImage -import coil.request.ImageRequest -import com.paw.key.core.designsystem.component.TopBar -import com.paw.key.core.designsystem.theme.PawKeyTheme -import com.paw.key.core.util.PreferenceDataStore -import com.paw.key.presentation.ui.mypage.viewmodel.PetProfileViewModel -import kotlinx.coroutines.flow.first - -@Composable -fun PetProfileRoute( - navigateUp : () -> Unit, - modifier: Modifier = Modifier, - viewModel: PetProfileViewModel = hiltViewModel() -) { - val state = viewModel.state.collectAsStateWithLifecycle() - val userId = PreferenceDataStore.getUserId() - - LaunchedEffect(Unit) { - viewModel.getPetProfiles(userId.first()) - } - - PetProfileScreen( - imageUrl = state.value.imageUrl, - name = state.value.name, - gender = state.value.gender, - breed = state.value.breed, - age = state.value.age, - isNeutered = state.value.isNeutered, - energyLevel = state.value.energyLevel, - socialLevel = state.value.socialLevel, - navigateUp = navigateUp, - modifier = modifier - ) -} - -@Composable -fun PetProfileScreen( - navigateUp: () -> Unit, - modifier: Modifier = Modifier, - imageUrl: String? = null, - name: String, - gender: String, - breed: String, - age: String, - isNeutered: Boolean, - energyLevel: String, - socialLevel: String -) { - // 여기서 바로 가공 - val displayGender = when (gender.uppercase()) { - "M" -> "남아" - "F" -> "여아" - else -> gender - } - - Column( - modifier = modifier - .fillMaxSize() - .padding(horizontal = 20.dp) - ) { - TopBar(title = "반려견 프로필", onBackClick = navigateUp) - - Spacer(modifier = Modifier.height(40.dp)) - - Box( - modifier = Modifier - .size(108.dp) - .align(Alignment.CenterHorizontally) - .clip(CircleShape) - .border(2.dp, PawKeyTheme.colors.green500, CircleShape) - ) { - AsyncImage( - model = ImageRequest.Builder(LocalContext.current) - .data(imageUrl) - .crossfade(true) - .build(), - contentDescription = null, - modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.Crop - ) - } - - Spacer(modifier = Modifier.height(36.dp)) - - PetProfileItem(label = "이름", value = name) - PetProfileItem(label = "성별", value = displayGender) - - if (isNeutered) { - Text( - text = "중성화했어요", - style = PawKeyTheme.typography.caption12Sb2, - color = PawKeyTheme.colors.gray300, - modifier = Modifier.padding(start = 16.dp, top = 8.dp, bottom = 8.dp) - ) - } else { - Text( - text = "중성화했어요", - style = PawKeyTheme.typography.caption12Sb2, - color = PawKeyTheme.colors.gray300, - modifier = Modifier.padding(start = 16.dp, top = 8.dp, bottom = 8.dp) - ) - } - - - PetProfileItem(label = "견종", value = breed) - PetProfileItem(label = "나이", value = age) - - Text( - text = "성향", - style = PawKeyTheme.typography.body14Sb, - modifier = Modifier.padding(start = 16.dp, top = 8.dp, bottom = 10.dp) - ) - - Row( - modifier = Modifier - .fillMaxWidth() - .padding(start = 16.dp), - horizontalArrangement = Arrangement.Start - ) { - Column( - modifier = Modifier.width(133.dp), - verticalArrangement = Arrangement.spacedBy(4.dp) - ) { - Text( - text = "에너지 레벨", - style = PawKeyTheme.typography.body14R, - color = PawKeyTheme.colors.gray600 - ) - Text( - text = energyLevel, - style = PawKeyTheme.typography.head18Sb, - color = PawKeyTheme.colors.green500 - ) - } - - Spacer(modifier = Modifier.width(62.dp)) - - Column( - modifier = Modifier.width(133.dp), - verticalArrangement = Arrangement.spacedBy(4.dp) - ) { - Text( - text = "사회성 레벨", - style = PawKeyTheme.typography.body14R, - color = PawKeyTheme.colors.gray600 - ) - Text( - text = socialLevel, - style = PawKeyTheme.typography.head18Sb, - color = PawKeyTheme.colors.green500 - ) - } - } - - Spacer(modifier = Modifier.weight(1f)) - } -} -@Composable -fun PetProfileItem( - label: String, - value: String -) { - Column( - modifier = Modifier - .padding(start = 16.dp, bottom = 8.dp) - ) { - Text( - text = label, - style = PawKeyTheme.typography.body14R, - color = PawKeyTheme.colors.gray600 - ) - Text( - text = value, - style = PawKeyTheme.typography.head18Sb, - color = PawKeyTheme.colors.green500 - ) - } -} - -@Preview(showBackground = true) -@Composable -fun PetProfileScreenPreview() { - PawKeyTheme { - PetProfileScreen( - name = "까루", - gender = "남아", - breed = "코리안 숏헤어", - age = "4세", - energyLevel = "활동적이에요", - socialLevel = "불편해해요", - imageUrl = null, - navigateUp = {}, - isNeutered = true - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/SavedCourseDetailScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/SavedCourseDetailScreen.kt deleted file mode 100644 index ed07ff70..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/SavedCourseDetailScreen.kt +++ /dev/null @@ -1,240 +0,0 @@ -package com.paw.key.presentation.ui.mypage - -import android.app.Activity -import android.util.Log -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material3.HorizontalDivider -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect -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.Color -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalView -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.zIndex -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.paw.key.R -import com.paw.key.core.designsystem.component.CourseDetail -import com.paw.key.core.designsystem.component.ImageModal -import com.paw.key.core.designsystem.component.PawkeyButton -import com.paw.key.core.designsystem.component.TopBar -import com.paw.key.core.designsystem.theme.PawKeyTheme -import com.paw.key.core.designsystem.theme.White1 -import com.paw.key.core.util.PreferenceDataStore -import com.paw.key.domain.model.entity.walklist.CategoryTop3Entity -import com.paw.key.presentation.ui.mypage.viewmodel.SavedDetailViewModel -import kotlinx.coroutines.flow.first - -@Composable -fun SavedDetailRoute( - navigateUp: () -> Unit, - navigateToSharedWalk: () -> Unit, - routeId : Int, - pageId : Int, - modifier: Modifier = Modifier, - viewModel: SavedDetailViewModel = hiltViewModel() -) { - val state by viewModel.state.collectAsStateWithLifecycle() - val userId = PreferenceDataStore.getUserId() - - LaunchedEffect(Unit) { - Log.e("SavedDetailRoute", "userId: ${userId.first()}, pageId: $pageId") - viewModel.getWalkDetail(userId.first(), pageId) - viewModel.getWalkTopPopular(userId.first(), routeId) - } - - SavedCourseDetailScreen( - title = state.postTitle, - petName = state.petName, - date = state.createdAt, - location = state.regionName, - isLike = state.isLiked, - content = state.postContent, - petProfileImage = state.petProfileImage, - routeMapImageUrl = state.routeMapImageUrl, - categorySummary = state.categorySummary, - categoryTop3 = state.categoryTop3, - totalReviewCount = state.totalReviewCount, - walkingImageUrls = state.walkingImageUrls, - clickImage = state.clickImage, - - onClickImage = { - viewModel.onClickImage(it) - }, - - navigateUp = navigateUp, - navigateToSharedWalk = navigateToSharedWalk, - modifier = modifier - ) -} - -@Composable -fun SavedCourseDetailScreen( - title: String, - petName: String, - date: String, - location: String, - isLike: Boolean, - content: String, - petProfileImage: String, - routeMapImageUrl: String, - categorySummary: List, - categoryTop3: List, - totalReviewCount: Int, - walkingImageUrls: List, - clickImage: String, - navigateUp: () -> Unit, - navigateToSharedWalk: () -> Unit, - onClickImage: (String) -> Unit, - modifier: Modifier = Modifier, -) { - var isImageExpanded by remember { mutableStateOf(false) } - val isLiked by remember { mutableStateOf(isLike) } - val view = LocalView.current - val statusBarColor = PawKeyTheme.colors.green500 - - val context = LocalContext.current - val window = (context as? Activity)?.window - val previousNavBarColor = remember { window?.navigationBarColor } - - DisposableEffect(Unit) { - window?.navigationBarColor = statusBarColor.toArgb() - - onDispose { - // 화면에서 벗어날 때 원래 색으로 복원 - previousNavBarColor?.let { - window?.navigationBarColor = it - } - } - } - - Column( - modifier = modifier - .fillMaxSize() - .background(color = White1) - ) { - TopBar( - title = "내가 기록한 산책 루트", - onBackClick = navigateUp - ) - - LazyColumn( - modifier = modifier - .fillMaxSize() - .background(color = Color.Transparent) - .zIndex(2F) - ) { - item { - CourseDetail( - title = title, - petName = petName, - date = date, - Icon = if(isLiked) R.drawable.ic_heart_default else R.drawable.ic_heart_filled, - location = location, - content = content, - onClickLike = {}, - petProfileImage = petProfileImage, - routeMapImageUrl = routeMapImageUrl, - categorySummary = categorySummary, - categoryTop3 = categoryTop3, - totalReviewCount = totalReviewCount, - walkingImageUrls = walkingImageUrls, - onImageClick = { - onClickImage(it) - isImageExpanded = true - } - ) - - Spacer(modifier = Modifier.height(36.dp)) - - PawkeyButton( - text = "해당 루트로 산책하기", - enabled = true, - onClick = navigateToSharedWalk, - modifier = Modifier - .fillMaxWidth() - .padding(top = 16.dp) - .background(color = PawKeyTheme.colors.green500) - ) - } - } - } - if (isImageExpanded) { - ImageModal( - imageUrl = clickImage, - onDismiss = { isImageExpanded = false } - ) - } -} - - -@Preview(showBackground = true) -@Composable -fun SavedCourseDetailPreview() { - PawKeyTheme { - SavedCourseDetailScreen( - title = "한강 산책로", - petName = "후추", - date = "2025/06/02", - location = "뚝섬유원지", - isLike = true, - content = "봄에 꽃이 만개한 산책로예요. 조용하고 평탄해서 걷기 좋아요.", - petProfileImage = "https://pawkey-server.com/profile.jpg", - routeMapImageUrl = "https://pawkey-server.com/map.jpg", - categorySummary = listOf("풍경이 좋아요", "조용해요", "길이 깨끗해요"), - categoryTop3 = listOf( - CategoryTop3Entity( - categoryId = 1, - categoryName = "안전", - categoryOptionId = 2, - optionText = "차량이 거의 다니지 않아요", - rank = 1, - percentage = 42 - ), - CategoryTop3Entity( - categoryId = 1, - categoryName = "안전", - categoryOptionId = 3, - optionText = "킥보드가 거의 없어요", - rank = 2, - percentage = 37 - ), - CategoryTop3Entity( - categoryId = 2, - categoryName = "편리성", - categoryOptionId = 7, - optionText = "조명이 밝아요", - rank = 3, - percentage = 35 - ) - ), - totalReviewCount = 42, - walkingImageUrls = listOf( - "https://pawkey-server.com/etc1.jpg", - "https://pawkey-server.com/etc2.jpg" - ), - navigateUp = {}, - navigateToSharedWalk = {}, - onClickImage = { _ -> }, - modifier = Modifier, - clickImage = "" - ) - } -} diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/SavedCourseListScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/SavedCourseListScreen.kt deleted file mode 100644 index 8d10d936..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/SavedCourseListScreen.kt +++ /dev/null @@ -1,112 +0,0 @@ -package com.paw.key.presentation.ui.mypage - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.paw.key.core.designsystem.component.CourseCard -import com.paw.key.core.designsystem.component.TopBar -import com.paw.key.core.designsystem.theme.PawKeyTheme -import com.paw.key.core.util.PreferenceDataStore -import com.paw.key.presentation.ui.mypage.state.SavedListState -import com.paw.key.presentation.ui.mypage.viewmodel.SavedListViewModel -import kotlinx.coroutines.flow.first - -@Composable -fun SavedCourseRoute( - navigateUp: () -> Unit, - navigateNext: (Int, Int) -> Unit, - modifier: Modifier = Modifier, - viewModel: SavedListViewModel = hiltViewModel() -) { - val state = viewModel.state.collectAsStateWithLifecycle() - val userId = PreferenceDataStore.getUserId() -// LaunchedEffect(Unit) { -// viewModel.getSavedList(userId = userId.first()) -// } - - - SavedCourseListScreen( - state = state.value, - navigateUp = navigateUp, - navigateNext = { routeId, pageId -> - navigateNext(routeId, pageId) - }, - /*onClickLike = { - //viewModel.toggleLike(postId = , isLiked = false) - },*/ - onClickItem = { - //navigateNext(state.) - }, - modifier = modifier - ) -} - -@Composable -fun SavedCourseListScreen( - state: SavedListState, - navigateUp: () -> Unit, - navigateNext: (Int, Int) -> Unit, - onClickItem: () -> Unit, - modifier: Modifier = Modifier -) { - Column( - modifier = modifier - .fillMaxSize() - .background(PawKeyTheme.colors.white2) - ) { - - TopBar( - title = "저장한 산책 루트", - onBackClick = navigateUp - ) - - LazyColumn( - modifier = modifier - .fillMaxWidth() - .padding(16.dp) - .background(PawKeyTheme.colors.white1) - ) { - itemsIndexed(state.courseList) { _, item -> - CourseCard( - postId = item.postId.toInt(), - title = item.title, - createdAt = item.createdAt, - representativeImageUrl = item.representativeImageUrl, - petName = item.writer.petName, - petProfileImageUrl = item.writer.petProfileImageUrl, - descriptionTags = item.descriptionTags, - isLiked = item.isLiked, - isPublic = item.isPublic, - isMine = item.isMine, - onClickItem = { - navigateNext(item.routeId, item.postId) - }, - ) - } - } - } -} - -@Preview(showBackground = true) -@Composable -fun SavedCourseListScreenPreview() { - PawKeyTheme { - SavedCourseListScreen( - state = SavedListState(), - navigateUp = {}, - navigateNext = { _, _ -> - }, - onClickItem = {} - - //onClickLike = {} - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/UserProfileScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/UserProfileScreen.kt deleted file mode 100644 index f453e0ff..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/UserProfileScreen.kt +++ /dev/null @@ -1,110 +0,0 @@ -package com.paw.key.presentation.ui.mypage - -import androidx.compose.foundation.layout.* -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.paw.key.core.designsystem.component.TopBar -import com.paw.key.core.designsystem.theme.PawKeyTheme -import com.paw.key.core.util.PreferenceDataStore -import com.paw.key.presentation.ui.mypage.viewmodel.UserProfileViewModel -import kotlinx.coroutines.flow.first - -@Composable -fun UserProfileRoute( - navigateUp: () -> Unit, - modifier: Modifier = Modifier, - viewModel: UserProfileViewModel = hiltViewModel() -) { - val state = viewModel.state.collectAsStateWithLifecycle() - val userId = PreferenceDataStore.getUserId() - - LaunchedEffect(Unit) { - viewModel.getUserProfiles(userId = userId.first()) - } - - UserProfileScreen( - name = state.value.name, - gender = state.value.gender, - age = state.value.age.toString(), - activeRegion = state.value.activeRegion, - navigateUp = navigateUp, - modifier = modifier - ) -} - -@Composable -fun UserProfileScreen(name: String, - gender: String, - age: String, - activeRegion: String, - navigateUp: () -> Unit, - modifier: Modifier = Modifier -) { - Column( - modifier = modifier - .fillMaxSize() - .padding(horizontal = 20.dp) - ) { - TopBar( - title = "견주 프로필", - onBackClick = { navigateUp() } - ) - - Spacer(modifier = Modifier.height(40.dp)) - - Column( - verticalArrangement = Arrangement.spacedBy(16.dp) - ) { - UserProfileItem(label = "이름", value = name) - UserProfileItem(label = "성별", value = if (gender == "M") { - "남아" - } else { - "여아" - }) - UserProfileItem(label = "나이", value = age) - UserProfileItem(label = "활동지역", value = activeRegion) - } - - Spacer(modifier = Modifier.weight(1f)) - } -} - -@Composable -fun UserProfileItem(label: String, value: String) { - Column(modifier = Modifier.padding(vertical = 8.dp)) { - Text( - text = label, - style = PawKeyTheme.typography.body14Sb, - modifier = Modifier.padding(start = 16.dp) - ) - if (value.isNotEmpty()) { - Spacer(modifier = Modifier.height(4.dp)) - Text( - text = value, - style = PawKeyTheme.typography.head18Sb, - color = PawKeyTheme.colors.green500, - modifier = Modifier.padding(start = 16.dp) - ) - } - } -} - -@Preview(showBackground = true) -@Composable -fun UserProfileScreenPreview() { - PawKeyTheme { - UserProfileScreen( - name = "김도기", - gender = "여성", - age = "24", - activeRegion = "강남구 역삼동", - navigateUp = {} - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/courseinfo/CourseInfoScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/courseinfo/CourseInfoScreen.kt new file mode 100644 index 00000000..b486e1d5 --- /dev/null +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/courseinfo/CourseInfoScreen.kt @@ -0,0 +1,97 @@ +package com.paw.key.presentation.ui.mypage.courseinfo + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.material3.HorizontalDivider +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.paw.key.core.designsystem.component.TopBar +import com.paw.key.core.designsystem.theme.PawKeyTheme +import com.paw.key.presentation.ui.mypage.courseinfo.component.CourseRouteItem +import com.paw.key.presentation.ui.mypage.courseinfo.model.CourseData +import com.paw.key.presentation.ui.mypage.courseinfo.model.CourseType + + +@Composable +fun CourseInfoRoute( + courseType: CourseType, + navigateUp: () -> Unit, + modifier: Modifier = Modifier, +) { + CourseInfoScreen( + title = courseType.courseType, + courses = emptyList(), + navigateUp = navigateUp, + modifier = modifier + ) +} + + +@Composable +fun CourseInfoScreen( + title: String, + courses: List, + navigateUp: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxSize() + .background( + color = PawKeyTheme.colors.background + ) + ) { + TopBar( + title = title, + onBackClick = navigateUp, + ) + + HorizontalDivider( + modifier = Modifier.fillMaxWidth(), + thickness = 2.dp, + color = PawKeyTheme.colors.defaultButton + ) + + LazyVerticalGrid( + modifier = Modifier.fillMaxSize(), + columns = GridCells.Fixed(2), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + contentPadding = PaddingValues(20.dp) + ) { + items(courses.size) { index -> + val course = courses[index] + CourseRouteItem( + location = course.location, + routeTitle = course.title, + routeImage = course.imageUrl, + routeDistance = course.distance, + routeTime = course.time, + routeDate = course.date, + modifier = Modifier + ) + } + } + } +} + + +@Preview(showBackground = true) +@Composable +private fun CourseInfoScreenPreview() { + PawKeyTheme { + CourseInfoScreen( + title = "", + courses = emptyList(), + navigateUp = { }, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/courseinfo/component/CourseRouteItem.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/courseinfo/component/CourseRouteItem.kt new file mode 100644 index 00000000..4c85ed1a --- /dev/null +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/courseinfo/component/CourseRouteItem.kt @@ -0,0 +1,190 @@ +package com.paw.key.presentation.ui.mypage.courseinfo.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +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.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +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.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import com.paw.key.R +import com.paw.key.core.designsystem.component.DogkyFilterBadge +import com.paw.key.core.designsystem.theme.PawKeyTheme +import com.paw.key.core.designsystem.theme.PretendardBold + +@Composable +fun CourseRouteItem( + location: String, + routeTitle: String, + routeImage: String, + routeDistance: String, + routeTime: String, + routeDate: String, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .aspectRatio(9f / 16f) + .background( + color = PawKeyTheme.colors.gray25, + shape = RoundedCornerShape(8.dp) + ) + .clip(RoundedCornerShape(8.dp)) + ) { + AsyncImage( + model = routeImage, + contentDescription = "routeImage", + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize() + ) + + DogkyFilterBadge( + location = location, + onLocationClick = {}, + horizontalPadding = 6, + verticalPadding = 5, + modifier = Modifier + .align(Alignment.TopStart) + .padding(8.dp) + ) + + IconButton( + onClick = { /*TODO*/ }, + modifier = Modifier + .align(Alignment.TopEnd) + ) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_heart_default), + contentDescription = "heart", + tint = Color.Unspecified + ) + } + } + + CourseRouteItemInfo( + routeTitle = routeTitle, + routeDistance = routeDistance, + routeTime = routeTime, + routeDate = routeDate + ) + } +} + +@Composable +fun CourseRouteItemInfo( + routeTitle: String, + routeDistance: String, + routeTime: String, + routeDate: String, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .padding( + top = 12.dp, + start = 4.dp, + end = 4.dp + ) + ) { + Text( + text = routeTitle, + style = PawKeyTheme.typography.bodyActive, + fontFamily = PretendardBold, + color = PawKeyTheme.colors.contents + ) + + Spacer(modifier = Modifier.height(4.dp)) + + Text( + text = "현재 거리로부터 ${routeDistance}km", + style = PawKeyTheme.typography.subButtonDefault, + color = PawKeyTheme.colors.defaultMiddle + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Row( + modifier = Modifier + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + CourseIconText( + icon = ImageVector.vectorResource(R.drawable.ic_calendar), + text = routeDate + ) + + CourseIconText( + icon = ImageVector.vectorResource(R.drawable.ic_alarm), + text = "${routeTime}min" + ) + } + } +} + +@Composable +fun CourseIconText( + icon: ImageVector, + text: String, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Icon( + imageVector = icon, + contentDescription = text, + tint = Color.Unspecified + ) + + Spacer(modifier = Modifier.width(16.dp)) + + Text( + text = text, + style = PawKeyTheme.typography.buttonSmall, + color = PawKeyTheme.colors.defaultMiddle + ) + } +} + +@Preview +@Composable +private fun CourseRouteItemPreview() { + PawKeyTheme { + CourseRouteItem( + routeImage = "", + routeDistance = "10", + routeTime = "10", + routeTitle = "강남구 역삼동", + routeDate = "2025/11/06", + location = "강남구 역삼동" + ) + } + +} diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/courseinfo/model/CourseType.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/courseinfo/model/CourseType.kt new file mode 100644 index 00000000..597c48f7 --- /dev/null +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/courseinfo/model/CourseType.kt @@ -0,0 +1,19 @@ +package com.paw.key.presentation.ui.mypage.courseinfo.model + +enum class CourseType( + val courseType: String, +) { + MyCourse(courseType = "내가 기록한 산책"), + AllCourse(courseType = "저장 목록"), + ReviewCourse(courseType = "내가 남김 후기") +} + + +data class CourseData( + val location: String, + val title: String, + val imageUrl: String, + val distance: String, + val time: String, + val date: String +) \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/courseinfo/navigation/CourseInfoNavigation.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/courseinfo/navigation/CourseInfoNavigation.kt new file mode 100644 index 00000000..c74d353b --- /dev/null +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/courseinfo/navigation/CourseInfoNavigation.kt @@ -0,0 +1,43 @@ +package com.paw.key.presentation.ui.mypage.courseinfo.navigation + +import androidx.compose.ui.Modifier +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import androidx.navigation.toRoute +import com.paw.key.presentation.ui.mypage.courseinfo.CourseInfoRoute +import com.paw.key.presentation.ui.mypage.courseinfo.model.CourseType +import kotlinx.serialization.Serializable + + +fun NavController.navigateCourseInfo( + courseType: CourseType, + navOptions: NavOptions? = null, +) { + navigate( + CourseInfo(courseType = courseType.name), + navOptions + ) +} + +fun NavGraphBuilder.courseInfoNavGraph( + navigateUp: () -> Unit, + modifier: Modifier = Modifier, +) { + composable { backStackEntry -> + val args = backStackEntry.toRoute() + val courseType = CourseType.valueOf(args.courseType) + + CourseInfoRoute( + courseType = courseType, + navigateUp = navigateUp, + modifier = modifier + ) + } +} + +@Serializable +data class CourseInfo( + val courseType: String, +) \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/main/MyPageScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/MyPageScreen.kt new file mode 100644 index 00000000..d727ca2a --- /dev/null +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/MyPageScreen.kt @@ -0,0 +1,164 @@ +package com.paw.key.presentation.ui.mypage.main + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.paw.key.core.designsystem.component.TopBar +import com.paw.key.core.designsystem.theme.PawKeyTheme +import com.paw.key.core.util.PreferenceDataStore +import com.paw.key.presentation.ui.mypage.courseinfo.model.CourseType +import com.paw.key.presentation.ui.mypage.main.component.MyList +import com.paw.key.presentation.ui.mypage.main.component.OwnerCard +import com.paw.key.presentation.ui.mypage.main.component.PetCard +import com.paw.key.presentation.ui.mypage.main.component.SettingList +import com.paw.key.presentation.ui.mypage.main.model.MyListState +import com.paw.key.presentation.ui.mypage.main.model.MyPageState +import com.paw.key.presentation.ui.mypage.main.viewmodel.MyPageViewModel +import kotlinx.coroutines.flow.first + +@Composable +fun MyPageRoute( + paddingValues: PaddingValues, + navigateUp: () -> Unit, + navigatePetProfile: () -> Unit, + navigateCourseInfo: (CourseType) -> Unit, + navigatePetProfileList: () -> Unit, + navigateUserProfile: () -> Unit, + modifier: Modifier = Modifier, + viewModel: MyPageViewModel = hiltViewModel(), +) { + val state = viewModel.state.collectAsStateWithLifecycle() + val userId = PreferenceDataStore.getUserId() + + LaunchedEffect(Unit) { + viewModel.getUserProfiles(userId = userId.first()) + viewModel.getPetProfiles(userId = userId.first()) + } + + MyPageScreen( + state = state.value, + paddingValues = paddingValues, + navigateUp = navigateUp, + navigatePetProfile = navigatePetProfile, + navigateCourseInfo = navigateCourseInfo, + navigatePetProfileList = navigatePetProfileList, + navigateUserProfile = navigateUserProfile, + modifier = modifier + ) +} + +@Composable +fun MyPageScreen( + state: MyPageState, + paddingValues: PaddingValues, + navigateUp: () -> Unit, + navigatePetProfile: () -> Unit, + navigateCourseInfo: (CourseType) -> Unit, + navigatePetProfileList: () -> Unit, + navigateUserProfile: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxSize() + .background(PawKeyTheme.colors.white2) + .padding(paddingValues) + ) { + TopBar( + title = "마이페이지", + onBackClick = navigateUp, + isBackVisible = false + ) + + LazyColumn( + modifier = modifier + .fillMaxSize() + .background(PawKeyTheme.colors.defaultButton) + .padding(horizontal = 16.dp, vertical = 18.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + + item { + OwnerCard( + ownerName = state.ownerName, + ownerEmail = state.petName, + navigateUserProfile = navigateUserProfile + ) + } + + item { + PetCard( + name = state.petName, + age = state.petAge, + gender = if (state.petGender == "M") { + "남아" + } else { + "여아" + }, + image = state.petImageUrl, + onPetClick = navigatePetProfileList + ) + } + + item { + MyList( + listTitle = "산책 루트 관리", + listContent = MyListState(), + onListClick = { index -> + val courseType = when (index) { + 0 -> CourseType.MyCourse + 1 -> CourseType.AllCourse + 2 -> CourseType.ReviewCourse + else -> CourseType.MyCourse + } + navigateCourseInfo(courseType) + } + ) + } + + item { + SettingList( + listTitle = "설정", + listContent = MyListState(), + onListClick = { } + ) + } + } + } +} + + +@Preview(showBackground = true) +@Composable +private fun MyPageScreenPreview() { + PawKeyTheme { + MyPageScreen( + state = MyPageState( + ownerName = "키큰오팔전차님", + petName = "포비", + petAge = "12세", + petGender = "여아", + petTags = listOf("조금 느긋해요", "#오토바이소리", "#대형견"), + walkCount = 7, + totalDistance = "14km" + ), + paddingValues = PaddingValues(), + navigateUp = {}, + navigatePetProfile = {}, + navigateCourseInfo = {}, + navigatePetProfileList = {}, + navigateUserProfile = {}, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/component/GrayChip.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/component/InfoChip.kt similarity index 50% rename from app/src/main/java/com/paw/key/presentation/ui/mypage/component/GrayChip.kt rename to app/src/main/java/com/paw/key/presentation/ui/mypage/main/component/InfoChip.kt index 08e8e598..cf63030d 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/component/GrayChip.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/component/InfoChip.kt @@ -1,4 +1,4 @@ -package com.paw.key.presentation.ui.mypage.component +package com.paw.key.presentation.ui.mypage.main.component import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -6,40 +6,41 @@ 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.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.paw.key.core.designsystem.theme.PawKeyTheme -import com.paw.key.core.extension.noRippleClickable -@Preview @Composable -private fun PreviewGrayChip() { - GrayChip( - text = "4km", - onClick = {} - ) -} - -@Composable -fun GrayChip( - text: String, +fun InfoChip( + chipText: String, modifier: Modifier = Modifier, - onClick : () -> Unit = {}, ) { Box( modifier = modifier .background( - color = PawKeyTheme.colors.white2, - shape = RoundedCornerShape(20.dp) + color = PawKeyTheme.colors.primary, + shape = RoundedCornerShape(4.dp) ) - .noRippleClickable(onClick = onClick) - .padding(horizontal = 10.dp, vertical = 4.dp) + .padding(horizontal = 6.dp, vertical = 5.dp), + contentAlignment = Alignment.Center ) { Text( - text = text, - color = PawKeyTheme.colors.gray400, - style = PawKeyTheme.typography.caption12R + text = chipText, + style = PawKeyTheme.typography.buttonSmall, + color = PawKeyTheme.colors.background + ) + } +} + + +@Preview(showBackground = true) +@Composable +private fun ReviewInfoChip() { + PawKeyTheme { + InfoChip( + chipText = "동네인기스타 ddd" ) } } \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/main/component/MyList.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/component/MyList.kt new file mode 100644 index 00000000..cf00ba18 --- /dev/null +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/component/MyList.kt @@ -0,0 +1,96 @@ +package com.paw.key.presentation.ui.mypage.main.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.paw.key.core.designsystem.theme.PawKeyTheme +import com.paw.key.presentation.ui.mypage.main.model.MyListState + +@Composable +fun MyList( + listTitle: String, + listContent: MyListState, + onListClick: (Int) -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxWidth() + .background( + color = PawKeyTheme.colors.background, + shape = RoundedCornerShape(16.dp) + ) + .padding(horizontal = 16.dp), + ) { + Spacer(modifier = Modifier.height(10.dp)) + + Text( + text = listTitle, + style = PawKeyTheme.typography.buttonSmall, + color = PawKeyTheme.colors.contents + ) + + listContent.myList.forEachIndexed { index, item -> + MyListItem( + title = item.title, + iconRes = item.iconRes, + onListClick = { onListClick(index) }, + ) + } + } +} + +@Composable +fun SettingList( + listTitle: String, + listContent: MyListState, + onListClick: (Int) -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxWidth() + .background( + color = PawKeyTheme.colors.background, + shape = RoundedCornerShape(16.dp) + ) + .padding(horizontal = 16.dp), + ) { + Spacer(modifier = Modifier.height(10.dp)) + + Text( + text = listTitle, + style = PawKeyTheme.typography.buttonSmall, + color = PawKeyTheme.colors.contents + ) + + listContent.settingList.forEachIndexed { index, item -> + MyListItem( + title = item.title, + iconRes = item.iconRes, + onListClick = { onListClick(index) }, + ) + } + } +} + +@Preview(showBackground = true) +@Composable +private fun ReviewMyList() { + PawKeyTheme { + MyList( + listTitle = "산책 루트 관리", + listContent = MyListState(), + onListClick = {} + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/main/component/MyListItem.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/component/MyListItem.kt new file mode 100644 index 00000000..190cd013 --- /dev/null +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/component/MyListItem.kt @@ -0,0 +1,74 @@ +package com.paw.key.presentation.ui.mypage.main.component + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +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.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.paw.key.R +import com.paw.key.core.designsystem.theme.PawKeyTheme +import com.paw.key.core.extension.noRippleClickable + +@Composable +fun MyListItem( + title: String, + @DrawableRes iconRes: Int, + onListClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .fillMaxWidth() + .background(color = PawKeyTheme.colors.background) + .noRippleClickable(onClick = onListClick) + .padding(horizontal = 16.dp, vertical = 16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Icon( + imageVector = ImageVector.vectorResource(id = iconRes), + contentDescription = "$title Icon", + tint = Color.Unspecified + ) + + Text( + text = title, + style = PawKeyTheme.typography.bodyDefault, + color = PawKeyTheme.colors.contents + ) + + Spacer(modifier = Modifier.weight(1f)) + + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_mypage_arrow_right), + contentDescription = "Arrow Right", + tint = Color.Unspecified + ) + } +} + + +@Preview(showBackground = true) +@Composable +private fun MyListItemPreview() { + PawKeyTheme { + MyListItem( + title = "내가 기록한 산책", + onListClick = {}, + iconRes = R.drawable.ic_mypage_edit, + modifier = Modifier.fillMaxWidth() + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/main/component/OwnerCard.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/component/OwnerCard.kt new file mode 100644 index 00000000..9a9ffb1f --- /dev/null +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/component/OwnerCard.kt @@ -0,0 +1,85 @@ +package com.paw.key.presentation.ui.mypage.main.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.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +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.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.paw.key.R +import com.paw.key.core.designsystem.theme.PawKeyTheme +import com.paw.key.core.extension.noRippleClickable + +@Composable +fun OwnerCard( + ownerName: String, + ownerEmail: String, + navigateUserProfile: () -> Unit, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .fillMaxWidth() + .background( + color = PawKeyTheme.colors.background, + shape = RoundedCornerShape(16.dp) + ) + .noRippleClickable( + onClick = navigateUserProfile + ) + .padding( + horizontal = 16.dp, vertical = 20.dp + ), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + Column( + modifier = Modifier, + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + Text( + text = ownerName, + style = PawKeyTheme.typography.mainButtonActive, + color = PawKeyTheme.colors.contents + ) + + Text( + text = ownerEmail, + style = PawKeyTheme.typography.subButtonDefault, + color = PawKeyTheme.colors.defaultMiddle + ) + } + + Spacer(modifier = Modifier.weight(1F)) + + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_mypage_arrow_right), + contentDescription = "owner profile", + tint = Color.Unspecified + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun ReviewOwnerCard() { + PawKeyTheme { + OwnerCard( + ownerName = "키큰오팔전차 님", + ownerEmail = "hell@gmail.com", + navigateUserProfile = {} + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/main/component/PetCard.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/component/PetCard.kt new file mode 100644 index 00000000..9b065255 --- /dev/null +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/component/PetCard.kt @@ -0,0 +1,129 @@ +package com.paw.key.presentation.ui.mypage.main.component + +import android.net.Uri +import androidx.compose.foundation.background +import androidx.compose.foundation.border +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.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +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.paw.key.core.designsystem.theme.PawKeyTheme +import com.paw.key.core.extension.noRippleClickable + +@Composable +fun PetCard( + name: String, + age: String, + gender: String, + image: Uri?, + onPetClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .border( + width = 1.dp, + color = PawKeyTheme.colors.primary, + shape = RoundedCornerShape(12.dp) + ) + .noRippleClickable(onClick = onPetClick) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .background(PawKeyTheme.colors.primary) + .padding(horizontal = 16.dp, vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "반려견 프로필", + style = PawKeyTheme.typography.body16Sb, + color = Color.White + ) + } + Row( + modifier = Modifier + .fillMaxWidth() + .background( + color = PawKeyTheme.colors.opacity5Primary, + ) + .padding( + horizontal = 16.dp, + vertical = 20.dp + ), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + AsyncImage( + model = ImageRequest.Builder(LocalContext.current) + .data(image) + .crossfade(true) + .build(), + contentDescription = null, + modifier = modifier + .size(64.dp) + .clip(CircleShape), + contentScale = ContentScale.Crop + ) + + Column( + modifier = Modifier, + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + InfoChip( + chipText = name, + ) + + Row( + modifier = Modifier, + horizontalArrangement = Arrangement.spacedBy(4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = name, + style = PawKeyTheme.typography.subTitle, + color = PawKeyTheme.colors.contents + ) + + Text( + "$age · $gender", + style = PawKeyTheme.typography.caption12R, + color = PawKeyTheme.colors.gray300 + ) + } + } + } + } +} + +@Preview(showBackground = true) +@Composable +private fun ReviewPetCard() { + PawKeyTheme { + PetCard( + name = "멍멍이", + age = "5살", + gender = "남아", + image = null, + onPetClick = {} + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/main/model/MyListState.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/model/MyListState.kt new file mode 100644 index 00000000..8d21f851 --- /dev/null +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/model/MyListState.kt @@ -0,0 +1,24 @@ +package com.paw.key.presentation.ui.mypage.main.model + +import androidx.compose.runtime.Immutable +import com.paw.key.R +import kotlinx.collections.immutable.toPersistentList + +@Immutable +data class MyListState( + val myList: List = MyList.entries.toPersistentList(), + val settingList: List = SettingList.entries.toPersistentList(), +) + +enum class MyList(val title: String, val iconRes: Int) { + MyPost("내가 기록한 산책", iconRes = R.drawable.ic_mypage_edit), + MyComment("저장목록", iconRes = R.drawable.ic_mypage_heart), + MyLike("내가 남긴 후기", iconRes = R.drawable.ic_mypage_mypost), +} + +enum class SettingList(val title: String, val iconRes: Int) { + ActiveRange(title = "활동 범위 설정", iconRes = R.drawable.ic_range_setting), + AppInfo(title = "앱 정보", iconRes = R.drawable.ic_mypage_app_inf), + Logout(title = "로그아웃", iconRes = R.drawable.ic_mypage_logout), + Delete(title = "회원탈퇴", iconRes = R.drawable.ic_mypage_delete) +} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/state/MyPageContract.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/model/MyPageContract.kt similarity index 84% rename from app/src/main/java/com/paw/key/presentation/ui/mypage/state/MyPageContract.kt rename to app/src/main/java/com/paw/key/presentation/ui/mypage/main/model/MyPageContract.kt index fd8f4628..f814846a 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/state/MyPageContract.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/model/MyPageContract.kt @@ -1,5 +1,6 @@ -package com.paw.key.presentation.ui.mypage.state +package com.paw.key.presentation.ui.mypage.main.model +import android.net.Uri import androidx.compose.runtime.Immutable @Immutable @@ -8,7 +9,7 @@ import androidx.compose.runtime.Immutable val petName: String = "포비", val petAge: String = "12세", val petGender: String = "여아", - val petImageUrl: String = "", + val petImageUrl: Uri ?= null, val petTags: List = listOf("조금 느긋해요", "#오토바이소리", "#대형견"), val walkCount: Int = 0, val totalDistance: String = "14km" diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/MyPageNavigation.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/navigation/MyPageNavigation.kt similarity index 72% rename from app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/MyPageNavigation.kt rename to app/src/main/java/com/paw/key/presentation/ui/mypage/main/navigation/MyPageNavigation.kt index 087a2efe..4fdd340d 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/MyPageNavigation.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/navigation/MyPageNavigation.kt @@ -1,4 +1,4 @@ -package com.paw.key.presentation.ui.mypage.navigation +package com.paw.key.presentation.ui.mypage.main.navigation import androidx.compose.foundation.layout.PaddingValues import androidx.compose.material3.SnackbarHostState @@ -8,7 +8,8 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable import com.paw.key.core.navigation.MainTabRoute -import com.paw.key.presentation.ui.mypage.MyPageRoute +import com.paw.key.presentation.ui.mypage.courseinfo.model.CourseType +import com.paw.key.presentation.ui.mypage.main.MyPageRoute import kotlinx.serialization.Serializable fun NavController.navigateMyPage( @@ -20,22 +21,20 @@ fun NavController.navigateMyPage( fun NavGraphBuilder.myPageNavGraph( paddingValues: PaddingValues, navigateUp: () -> Unit, - navigateUserProfile: () -> Unit, navigatePetProfile: () -> Unit, - navigateArchivedCourse: () -> Unit, - navigateSavedCourse: () -> Unit, - snackBarHostState: SnackbarHostState, + navigateCourseInfo: (CourseType) -> Unit, + navigatePetProfileList: () -> Unit, + navigateUserProfile: () -> Unit, modifier: Modifier = Modifier ) { composable { MyPageRoute( paddingValues = paddingValues, navigateUp = navigateUp, - navigateUserProfile = navigateUserProfile, navigatePetProfile = navigatePetProfile, - navigateArchivedCourse = navigateArchivedCourse, - navigateSavedCourse = navigateSavedCourse, - snackBarHostState = snackBarHostState, + navigateCourseInfo = navigateCourseInfo, + navigatePetProfileList = navigatePetProfileList, + navigateUserProfile = navigateUserProfile, modifier = modifier ) } diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/MyPageViewModel.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/viewmodel/MyPageViewModel.kt similarity index 88% rename from app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/MyPageViewModel.kt rename to app/src/main/java/com/paw/key/presentation/ui/mypage/main/viewmodel/MyPageViewModel.kt index aec2867d..fa0f1059 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/MyPageViewModel.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/main/viewmodel/MyPageViewModel.kt @@ -1,13 +1,11 @@ -package com.paw.key.presentation.ui.mypage.viewmodel +package com.paw.key.presentation.ui.mypage.main.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.paw.key.domain.repository.SavedListRepository import com.paw.key.domain.repository.petprofile.PetProfileRepository import com.paw.key.domain.repository.userprofile.UserProfileRepository -import com.paw.key.presentation.ui.mypage.state.MyPageSideEffect -import com.paw.key.presentation.ui.mypage.state.MyPageState -import com.paw.key.presentation.ui.mypage.state.PetProfileSideEffect +import com.paw.key.presentation.ui.mypage.main.model.MyPageSideEffect +import com.paw.key.presentation.ui.mypage.main.model.MyPageState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/ArchivedDetailNavigation.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/ArchivedDetailNavigation.kt deleted file mode 100644 index db2eee7a..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/ArchivedDetailNavigation.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.paw.key.presentation.ui.mypage.navigation - -import androidx.compose.ui.Modifier -import androidx.navigation.NavController -import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavOptions -import androidx.navigation.compose.composable -import androidx.navigation.toRoute -import com.paw.key.core.navigation.Route -import com.paw.key.presentation.ui.mypage.ArchivedDetailRoute -import kotlinx.serialization.Serializable - -fun NavController.navigateArchivedDetail( - routeId: Int, - pageId : Int, - navOptions: NavOptions? -) { - navigate(ArchivedDetail(routeId, pageId), navOptions) -} - -fun NavGraphBuilder.archivedDetailNavGraph( - navigateUp: () -> Unit, - //navigateDetail: () -> Unit, - navigateToSharedWalk: (Int, Int) -> Unit, - modifier: Modifier = Modifier, -) { - composable { backStackEntry -> - val archivedDetail = backStackEntry.toRoute() - - ArchivedDetailRoute( - navigateUp = navigateUp, - navigateToSharedWalk = { routeId, pageId -> - navigateToSharedWalk(routeId, pageId) - }, - //navigateDetail = navigateDetail, - routeId = archivedDetail.routeId, - pageId = archivedDetail.pageId, - modifier = modifier - ) - } -} - -@Serializable -data class ArchivedDetail(val routeId: Int, val pageId : Int) : Route \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/SavedCourseNavigation.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/SavedCourseNavigation.kt deleted file mode 100644 index 1f3d1208..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/SavedCourseNavigation.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.paw.key.presentation.ui.mypage.navigation - -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.material3.SnackbarHostState -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.navigation.NavController -import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavOptions -import androidx.navigation.compose.composable -import androidx.navigation.toRoute -import com.paw.key.core.navigation.Route -import com.paw.key.presentation.ui.mypage.SavedCourseRoute -import kotlinx.serialization.Serializable - -fun NavController.navigateSavedCourse( - navOptions: NavOptions?, -) { - navigate(SavedCourse, navOptions) -} - -fun NavGraphBuilder.savedCourseNavGraph( - paddingValues: PaddingValues, - navigateUp: () -> Unit, - navigateNext: (Int, Int) -> Unit, - snackBarHostState: SnackbarHostState, - modifier: Modifier = Modifier, -) { - composable { backStackEntry -> - val ids = backStackEntry.toRoute() - - SavedCourseRoute( - navigateUp = navigateUp, - navigateNext = { routeId, pageId -> - navigateNext(routeId, pageId) - }, - modifier = modifier - ) - } -} - -@Serializable -data object SavedCourse : Route \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/SavedDetailNavigation.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/SavedDetailNavigation.kt deleted file mode 100644 index 00102bf9..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/SavedDetailNavigation.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.paw.key.presentation.ui.mypage.navigation - -import com.paw.key.presentation.ui.mypage.SavedDetailRoute -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.material3.SnackbarHostState -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.navigation.NavController -import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavOptions -import androidx.navigation.compose.composable -import androidx.navigation.toRoute -import com.paw.key.core.navigation.Route -import com.paw.key.presentation.ui.mypage.SavedCourseRoute -import kotlinx.serialization.Serializable - -fun NavController.navigateSavedDetail( - navOptions: NavOptions?, - routeId : Int, - pageId : Int -) { - navigate(SavedDetail(routeId, pageId), navOptions) -} - -fun NavGraphBuilder.savedDetailNavGraph( - navigateUp: () -> Unit, - navigateToWalk : () -> Unit, - snackBarHostState: SnackbarHostState, - modifier: Modifier = Modifier, -) { - composable { backStackEntry -> - val ids = backStackEntry.toRoute() - - SavedDetailRoute( - navigateUp = navigateUp, - navigateToSharedWalk = navigateToWalk, - routeId = ids.routeId, - pageId = ids.pageId, - modifier = modifier - ) - } -} - -@Serializable -data class SavedDetail(val routeId : Int, val pageId : Int) : Route \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/PetProfileListScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/PetProfileListScreen.kt new file mode 100644 index 00000000..3170d2e3 --- /dev/null +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/PetProfileListScreen.kt @@ -0,0 +1,80 @@ +package com.paw.key.presentation.ui.mypage.petinfo + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +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 +import androidx.hilt.navigation.compose.hiltViewModel +import com.paw.key.core.designsystem.component.TopBar +import com.paw.key.core.designsystem.theme.PawKeyTheme +import com.paw.key.presentation.ui.mypage.petinfo.component.PetInfoCard +import com.paw.key.presentation.ui.mypage.petinfo.viewmodel.PetProfileViewModel + + +@Composable +fun PetProfileListRoute( + navigateUp: () -> Unit, + navigatePetProfile: () -> Unit, + modifier: Modifier = Modifier, + viewModel: PetProfileViewModel = hiltViewModel(), +) { + PetProfileListScreen( + navigateUp = navigateUp, + navigatePetProfile = navigatePetProfile, + ) +} + +@Composable +fun PetProfileListScreen( + navigateUp: () -> Unit, + navigatePetProfile: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxSize() + .background( + color = PawKeyTheme.colors.defaultButton + ), + verticalArrangement = Arrangement.spacedBy(20.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + TopBar( + title = "반려견 정보 수정", + onBackClick = navigateUp, + ) + + LazyColumn( + modifier = Modifier + .padding(horizontal = 16.dp), + ) { + item { + PetInfoCard( + petName = "Buddy", + petType = "Dog", + petImage = "https://example.com/buddy.jpg", + onPetClick = navigatePetProfile, + ) + } + } + } +} + + +@Preview(showBackground = true) +@Composable +private fun ReviewPetProfileListScreen() { + PawKeyTheme { + PetProfileListScreen( + navigateUp = { }, + navigatePetProfile = { }, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/PetProfileScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/PetProfileScreen.kt new file mode 100644 index 00000000..29fb071a --- /dev/null +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/PetProfileScreen.kt @@ -0,0 +1,353 @@ +package com.paw.key.presentation.ui.mypage.petinfo + +import android.Manifest +import android.net.Uri +import android.os.Build +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.PickVisualMediaRequest +import androidx.activity.result.contract.ActivityResultContracts +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.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.paw.key.R +import com.paw.key.core.designsystem.component.PawKeyBottomSheet +import com.paw.key.core.designsystem.component.PawkeyButton +import com.paw.key.core.designsystem.component.TopBar +import com.paw.key.core.designsystem.theme.PawKeyTheme +import com.paw.key.core.extension.noRippleClickable +import com.paw.key.core.util.PreferenceDataStore +import com.paw.key.presentation.ui.mypage.petinfo.viewmodel.PetProfileViewModel +import com.paw.key.presentation.ui.signup.component.FormField +import com.paw.key.presentation.ui.signup.component.GenderSelector +import com.paw.key.presentation.ui.signup.component.PetBreedSearchContent +import com.paw.key.presentation.ui.signup.component.SignUpNeuteringCheckRadio +import com.paw.key.presentation.ui.signup.component.SignUpPetImageHolder +import com.paw.key.presentation.ui.signup.component.SignUpTextField +import com.paw.key.presentation.ui.signup.state.Gender +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch + +@Composable +fun PetProfileRoute( + navigateUp: () -> Unit, + modifier: Modifier = Modifier, + viewModel: PetProfileViewModel = hiltViewModel(), +) { + val state = viewModel.state.collectAsStateWithLifecycle() + val userId = PreferenceDataStore.getUserId() + + LaunchedEffect(Unit) { + viewModel.getPetProfiles(userId.first()) + } + + PetProfileScreen( + petName = state.value.name, + petBirthDate = state.value.birthday, + petGender = Gender.MALE, + petNeutered = state.value.isNeutered, + petBreed = state.value.breed, + selectedImageUri = state.value.imageUrl, + navigateUp = navigateUp, + deniedPermission = {}, + onPetNameChanged = {}, + onPetBirthDateChanged = {}, + onPetGenderChanged = {}, + onPetNeuteredChanged = {}, + onPetBreedChanged = {}, + onSelectedImage = {} + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun PetProfileScreen( + petName: String, + petBirthDate: String, + petGender: Gender, + petNeutered: Boolean, + petBreed: String, + selectedImageUri: Uri?, + navigateUp: () -> Unit, + deniedPermission: () -> Unit, + onPetNameChanged: (String) -> Unit, + onPetBirthDateChanged: (String) -> Unit, + onPetGenderChanged: (Gender) -> Unit, + onPetNeuteredChanged: (Boolean) -> Unit, + onPetBreedChanged: (String) -> Unit, + onSelectedImage: (Uri?) -> Unit, + modifier: Modifier = Modifier, +) { + var isSheetOpen by remember { mutableStateOf(false) } + val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + val scope = rememberCoroutineScope() + + val petBirthDateFocusRequester = remember { FocusRequester() } + val focusManager = LocalFocusManager.current + + val photoPickerLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.PickVisualMedia(), + onResult = { uri -> + onSelectedImage(uri) + } + ) + + // 구버전 권한 요청용 + val legacyGalleryLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.GetContent(), + onResult = { uri -> + onSelectedImage(uri) + } + ) + + val permissionLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.RequestPermission(), + onResult = { isGranted -> + if (isGranted) { + legacyGalleryLauncher.launch("image/*") + } else { + deniedPermission + } + } + ) + + + + Column( + modifier = modifier + .fillMaxSize() + .background(color = PawKeyTheme.colors.background) + ) { + TopBar( + title = " 반려견 정보 수정", + onBackClick = navigateUp, + modifier = Modifier, + ) + + HorizontalDivider( + modifier = Modifier + .fillMaxWidth(), + thickness = 2.dp, + color = PawKeyTheme.colors.defaultButton + ) + + LazyColumn( + modifier = Modifier + .padding(horizontal = 16.dp, vertical = 18.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + item { + SignUpPetImageHolder( + uri = selectedImageUri, + modifier = Modifier + .noRippleClickable { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + photoPickerLauncher.launch( + PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly) + ) + } else { + permissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE) + } + } + ) + + } + + item { + FormField( + label = "이름", + content = { + SignUpTextField( + value = petName, + onValueChange = { + if (it.length <= 8) { + onPetNameChanged(it) + } + }, + placeholder = "최대 8글자 이내로 입력해주세요", + keyboardOptions = KeyboardOptions( + imeAction = ImeAction.Next + ), + keyboardActions = KeyboardActions( + onDone = { + petBirthDateFocusRequester.requestFocus() + } + ), + ) + } + ) + } + + item { + FormField( + label = "생년월일", + content = { + SignUpTextField( + modifier = Modifier + .focusRequester(petBirthDateFocusRequester), + value = petBirthDate, + onValueChange = { + if (it.length <= 8) { + onPetBirthDateChanged(it) + } + }, + placeholder = "YYYYMMDD", + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Done + ), + keyboardActions = KeyboardActions( + onDone = { + focusManager.clearFocus() + } + ), +// visualTransformation = DateVisualTransformation() + ) + } + ) + + } + item { + FormField( + label = "성별", + content = { + GenderSelector( + selectedGender = petGender, + onGenderSelected = onPetGenderChanged, + type = "반려 동물" + ) + } + ) + + } + + item { + SignUpNeuteringCheckRadio( + isNeutered = petNeutered, + onToggle = { onPetNeuteredChanged(!petNeutered) }, + modifier = Modifier + .padding(top = 8.dp) + ) + + } + + item { + + FormField( + label = "견종", + content = { + SignUpTextField( + value = petBreed, + onValueChange = onPetBreedChanged, + enabled = false, + placeholder = "견종을 검색해보세요", + suffix = { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_signup_search), + contentDescription = "breed search", + tint = Color.Unspecified + ) + }, + modifier = Modifier + .noRippleClickable { + scope.launch { + isSheetOpen = true + } + } + ) + } + ) + if (isSheetOpen) { + PawKeyBottomSheet( + onDismissRequest = { isSheetOpen = false }, + sheetState = sheetState, + //sheetGesturesEnabled = false, + ) { sheetState -> + PetBreedSearchContent( + sheetState = sheetState, + selectedBreed = petBreed, + onBreedSelected = { + onPetBreedChanged(it) + scope.launch { + + sheetState.hide() + }.invokeOnCompletion { + if (!sheetState.isVisible) { + isSheetOpen = false + } + } + }, + ) + } + } + } + } + + Spacer(modifier = Modifier.weight(1F)) + + PawkeyButton( + text = "저장하기", + enabled = true, + onClick = { }, + modifier = Modifier + .padding(horizontal = 16.dp) + ) + + Spacer(modifier = Modifier.height(34.dp)) + + } +} + +@Preview(showBackground = true) +@Composable +fun PetProfileScreenPreview() { + PawKeyTheme { + PetProfileScreen( + petName = "꾸꾸", + petBirthDate = "꾸꾸", + petGender = Gender.MALE, + petNeutered = true, + petBreed = "꾸꾸", + selectedImageUri = null, + navigateUp = {}, + deniedPermission = {}, + onPetNameChanged = {}, + onPetBirthDateChanged = {}, + onPetGenderChanged = {}, + onPetNeuteredChanged = {}, + onPetBreedChanged = {}, + onSelectedImage = {} + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/component/PetInfoCard.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/component/PetInfoCard.kt new file mode 100644 index 00000000..d91bca28 --- /dev/null +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/component/PetInfoCard.kt @@ -0,0 +1,127 @@ +package com.paw.key.presentation.ui.mypage.petinfo.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +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.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.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.paw.key.core.designsystem.theme.PawKeyTheme +import com.paw.key.core.extension.noRippleClickable + + +@Composable +fun PetInfoCard( + petName: String, + petType: String, + petImage: String, + onPetClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxWidth() + .background( + color = PawKeyTheme.colors.background, + shape = RoundedCornerShape(16.dp) + ) + .padding( + horizontal = 16.dp, + vertical = 16.dp + ), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + AsyncImage( + model = ImageRequest.Builder(LocalContext.current) + .data(petImage) + .crossfade(true) + .build(), + contentDescription = null, + modifier = modifier + .size(60.dp) + .clip(CircleShape), + contentScale = ContentScale.Crop + ) + + Column( + + ) { + Text( + text = petName, + style = PawKeyTheme.typography.subTitle, + color = PawKeyTheme.colors.contents + ) + + Text( + text = petType, + style = PawKeyTheme.typography.subButtonDefault, + color = PawKeyTheme.colors.defaultMiddle + ) + } + + } + + PetInfoButton( + onPetClick = onPetClick, + ) + } +} + +@Composable +private fun PetInfoButton( + onPetClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Box( + modifier = modifier + .fillMaxWidth() + .background( + color = PawKeyTheme.colors.defaultButton, + shape = RoundedCornerShape(4.dp) + ) + .noRippleClickable(onPetClick) + .padding(horizontal = 16.dp, vertical = 10.dp), + contentAlignment = Alignment.Center + ) { + Text( + text = "프로필 수정", + style = PawKeyTheme.typography.subButtonActive, + color = PawKeyTheme.colors.contents + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun ReviewPetInfoCard() { + PawKeyTheme { + PetInfoCard( + petName = "멍멍이", + petType = "강아지", + petImage = "https://example.com/dog.jpg", + modifier = Modifier.fillMaxWidth(), + onPetClick = {} + ) + } +} diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/state/PetProfileContract.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/model/PetProfileContract.kt similarity index 79% rename from app/src/main/java/com/paw/key/presentation/ui/mypage/state/PetProfileContract.kt rename to app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/model/PetProfileContract.kt index e6aa2bdf..89d23be0 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/state/PetProfileContract.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/model/PetProfileContract.kt @@ -1,12 +1,14 @@ -package com.paw.key.presentation.ui.mypage.state +package com.paw.key.presentation.ui.mypage.petinfo.model +import android.net.Uri import androidx.compose.runtime.Immutable @Immutable data class PetProfileState( - val imageUrl: String? = null, + val imageUrl: Uri? = null, val name: String = "까루", val gender: String = "남아", + val birthday : String = "2020/01/01", val breed: String = "코리안 숏헤어", val age: String = "4세", val isNeutered: Boolean = true, diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/ArchivedCourseNavigation.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/navigation/PetProfileListNavigation.kt similarity index 50% rename from app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/ArchivedCourseNavigation.kt rename to app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/navigation/PetProfileListNavigation.kt index 911f8636..2c3e062e 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/ArchivedCourseNavigation.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/navigation/PetProfileListNavigation.kt @@ -1,4 +1,4 @@ -package com.paw.key.presentation.ui.mypage.navigation +package com.paw.key.presentation.ui.mypage.petinfo.navigation import androidx.compose.ui.Modifier import androidx.navigation.NavController @@ -6,31 +6,28 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable import com.paw.key.core.navigation.Route -import com.paw.key.presentation.ui.mypage.ArchivedCourseRoute +import com.paw.key.presentation.ui.mypage.petinfo.PetProfileListRoute import kotlinx.serialization.Serializable -fun NavController.navigateArchivedCourse( - +fun NavController.navigatePetProfileList( navOptions: NavOptions? ) { - navigate(ArchivedCourse, navOptions) + navigate(PetProfileList, navOptions) } -fun NavGraphBuilder.archivedCourseNavGraph( +fun NavGraphBuilder.petProfileListNavGraph( navigateUp: () -> Unit, - navigateNext: (Int, Int) -> Unit, + navigatePetProfile : () -> Unit, modifier: Modifier = Modifier, ) { - composable { - ArchivedCourseRoute( + composable { + PetProfileListRoute( navigateUp = navigateUp, - navigateNext = { routeId, pageId -> - navigateNext(routeId, pageId) - }, + navigatePetProfile = navigatePetProfile, modifier = modifier ) } } @Serializable -data object ArchivedCourse : Route \ No newline at end of file +data object PetProfileList : Route \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/PetProfileNavigation.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/navigation/PetProfileNavigation.kt similarity index 84% rename from app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/PetProfileNavigation.kt rename to app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/navigation/PetProfileNavigation.kt index 68f6e5bc..e540ad81 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/PetProfileNavigation.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/navigation/PetProfileNavigation.kt @@ -1,4 +1,4 @@ -package com.paw.key.presentation.ui.mypage.navigation +package com.paw.key.presentation.ui.mypage.petinfo.navigation import androidx.compose.ui.Modifier import androidx.navigation.NavController @@ -6,7 +6,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable import com.paw.key.core.navigation.Route -import com.paw.key.presentation.ui.mypage.PetProfileRoute +import com.paw.key.presentation.ui.mypage.petinfo.PetProfileRoute import kotlinx.serialization.Serializable fun NavController.navigatePetProfile( diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/PetProfileViewModel.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/viewmodel/PetProfileViewModel.kt similarity index 92% rename from app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/PetProfileViewModel.kt rename to app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/viewmodel/PetProfileViewModel.kt index 9804253a..67500236 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/PetProfileViewModel.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/petinfo/viewmodel/PetProfileViewModel.kt @@ -1,11 +1,11 @@ -package com.paw.key.presentation.ui.mypage.viewmodel +package com.paw.key.presentation.ui.mypage.petinfo.viewmodel import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.paw.key.domain.repository.petprofile.PetProfileRepository -import com.paw.key.presentation.ui.mypage.state.PetProfileSideEffect -import com.paw.key.presentation.ui.mypage.state.PetProfileState +import com.paw.key.presentation.ui.mypage.petinfo.model.PetProfileSideEffect +import com.paw.key.presentation.ui.mypage.petinfo.model.PetProfileState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/state/ArchivedDetailContract.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/state/ArchivedDetailContract.kt deleted file mode 100644 index aafb7db4..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/state/ArchivedDetailContract.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.paw.key.presentation.ui.mypage.state - -import androidx.compose.runtime.Immutable -import com.paw.key.domain.model.entity.walklist.CategoryTop3Entity - -@Immutable -data class ArchivedDetailState( - val postId: Int = 0, - val routeId: Int = 0, - val postTitle: String = "", - val postContent: String = "", - val isPublic : Boolean = false, - val isLiked: Boolean = false, - val petName: String = "", - val petProfileImage: String = "", - val regionName: String = "", - val createdAt: String = "", - val categorySummary: List = emptyList(), - val routeMapImageUrl: String = "", - val walkingImageUrls: List = emptyList(), - - val categoryTop3: List = emptyList(), - val totalReviewCount: Int = 0, - - val clickImage : String = "" -) diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/state/ArchivedListContract.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/state/ArchivedListContract.kt deleted file mode 100644 index f84c7b62..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/state/ArchivedListContract.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.paw.key.presentation.ui.mypage.state - -import androidx.compose.runtime.Immutable -import com.paw.key.domain.model.entity.archivedlist.ArchivedListEntity -import com.paw.key.domain.model.entity.savedlist.SavedListEntity - -@Immutable -data class ArchivedListState( - val courseList: List = emptyList() -) - -//@Immutable -//data class CourseCardData( -// //제목 -// val description: String, -// val petName: String, -// val createdAt: String, -// val isShared: Boolean, -// val isLiked: Boolean, -// val imageUrl: String, -//) - -sealed class ArchivedListSideEffect { - data class ShowSnackBar(val message: String) : ArchivedListSideEffect() - data object NavigateUp : ArchivedListSideEffect() - data object NavigateNext : ArchivedListSideEffect() -} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/state/SavedDetailContract.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/state/SavedDetailContract.kt deleted file mode 100644 index e00f613e..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/state/SavedDetailContract.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.paw.key.presentation.ui.mypage.state - -import androidx.compose.runtime.Immutable -import com.paw.key.domain.model.entity.walklist.CategoryTop3Entity - -@Immutable -data class SavedDetailState( - val postId: Int = 0, - val routeId: Int = 0, - val postTitle: String = "", - val postContent: String = "", - val isPublic : Boolean = false, - val isLiked: Boolean = false, - val petName: String = "", - val petProfileImage: String = "", - val regionName: String = "", - val createdAt: String = "", - val categorySummary: List = emptyList(), - val routeMapImageUrl: String = "", - val walkingImageUrls: List = emptyList(), - - val categoryTop3: List = emptyList(), - val totalReviewCount: Int = 0, - - val clickImage : String = "" -) \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/state/SavedListContract.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/state/SavedListContract.kt deleted file mode 100644 index ab06110b..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/state/SavedListContract.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.paw.key.presentation.ui.mypage.state - -import androidx.compose.runtime.Immutable -import com.paw.key.domain.model.entity.savedlist.SavedListEntity -import com.paw.key.domain.model.entity.savedlist.SavedListPostEntity - -@Immutable -data class SavedListState( - val courseList: List = emptyList() -) - -@Immutable -data class CourseCardData( - //제목 - val description: String, - val petName: String, - val createdAt: String, - val isShared: Boolean, - val isLiked: Boolean, - val imageUrl: String, -) - -sealed class SavedListSideEffect { - data class ShowSnackBar(val message: String) : SavedListSideEffect() - data object NavigateUp : SavedListSideEffect() - data object NavigateNext : SavedListSideEffect() -} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/UserProfileScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/UserProfileScreen.kt new file mode 100644 index 00000000..d6cc9a60 --- /dev/null +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/UserProfileScreen.kt @@ -0,0 +1,149 @@ +package com.paw.key.presentation.ui.mypage.userinfo + +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.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.paw.key.core.designsystem.component.PawkeyButton +import com.paw.key.core.designsystem.component.TopBar +import com.paw.key.core.designsystem.theme.PawKeyTheme +import com.paw.key.core.util.PreferenceDataStore +import com.paw.key.presentation.ui.mypage.userinfo.component.UserEditTextField +import com.paw.key.presentation.ui.mypage.userinfo.component.UserGenderButton +import com.paw.key.presentation.ui.mypage.userinfo.component.UserProfileItem +import com.paw.key.presentation.ui.mypage.userinfo.viewmodel.UserProfileViewModel +import kotlinx.coroutines.flow.first + +@Composable +fun UserProfileRoute( + navigateUp: () -> Unit, + modifier: Modifier = Modifier, + viewModel: UserProfileViewModel = hiltViewModel(), +) { + val state = viewModel.state.collectAsStateWithLifecycle() + val userId = PreferenceDataStore.getUserId() + + LaunchedEffect(Unit) { + viewModel.getUserProfiles(userId = userId.first()) + } + + UserProfileScreen( + name = state.value.name, + gender = state.value.gender, + birth = state.value.name, + navigateUp = navigateUp, + modifier = modifier + ) +} + +@Composable +private fun UserProfileScreen( + name: String, + gender: String, + birth: String, + navigateUp: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxSize(), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + TopBar( + title = "내 정보 수정", + onBackClick = navigateUp + ) + + + Spacer(modifier = Modifier.height(4.dp)) + + UserProfileItem( + label = "닉네임", + profileItem = { + UserEditTextField( + value = name, + onValueChange = {}, + placeholder = "닉네임을 입력해주세요", + modifier = Modifier.fillMaxWidth(), + enabled = true, + singleLine = true + ) + } + ) + + UserProfileItem( + label = "생년원일", + profileItem = { + UserEditTextField( + value = birth, + onValueChange = {}, + placeholder = "닉네임을 입력해주세요", + modifier = Modifier.fillMaxWidth(), + enabled = true, + singleLine = true + ) + } + ) + + UserProfileItem( + label = "성별", + profileItem = { + Row( + horizontalArrangement = Arrangement.spacedBy(4.dp), + modifier = modifier.fillMaxWidth() + ) { + UserGenderButton( + user = "남성", + isSelect = gender == "남성", + onClick = { }, + modifier = Modifier + .weight(1f) + ) + UserGenderButton( + user = "여성", + isSelect = gender == "여성", + onClick = { }, + modifier = Modifier + .weight(1f) + ) + } + } + ) + + Spacer(modifier = Modifier.weight(1F)) + + PawkeyButton( + text = "저장하기", + enabled = true, + onClick = { }, + modifier = Modifier + .padding(horizontal = 16.dp) + ) + + Spacer(modifier = Modifier.height(34.dp)) + } +} + +@Preview(showBackground = true) +@Composable +private fun UserProfileScreenPreview() { + PawKeyTheme { + UserProfileScreen( + name = "김도기", + gender = "여성", + birth = "2002/06/21", + navigateUp = {} + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/component/UserEditTextField.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/component/UserEditTextField.kt new file mode 100644 index 00000000..87782ac3 --- /dev/null +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/component/UserEditTextField.kt @@ -0,0 +1,120 @@ +package com.paw.key.presentation.ui.mypage.userinfo.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 +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.text.selection.LocalTextSelectionColors +import androidx.compose.foundation.text.selection.TextSelectionColors +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.mutableStateOf +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.focus.onFocusChanged +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.paw.key.core.designsystem.theme.PawKeyTheme + +@Composable +fun UserEditTextField( + value: String, + onValueChange: (String) -> Unit, + placeholder: String, + modifier: Modifier = Modifier, + enabled: Boolean = true, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + visualTransformation: VisualTransformation = VisualTransformation.None, + singleLine: Boolean = true, + suffix: @Composable (() -> Unit)? = null, +) { + val isFocused = remember { mutableStateOf(false) } + + val borderColor = when { + !enabled -> PawKeyTheme.colors.defaultMiddle + isFocused.value -> PawKeyTheme.colors.primary + else -> PawKeyTheme.colors.defaultMiddle + } + + // Todo : Gra로 변경 + val customTextSelectionColors = TextSelectionColors( + handleColor = PawKeyTheme.colors.primary, + backgroundColor = PawKeyTheme.colors.primary.copy(alpha = 0.4f) + ) + + CompositionLocalProvider(LocalTextSelectionColors provides customTextSelectionColors) { + BasicTextField( + value = value, + onValueChange = onValueChange, + visualTransformation = visualTransformation, + modifier = modifier + .fillMaxWidth() + .clip(RoundedCornerShape(8.dp)) + .onFocusChanged { focusState -> + isFocused.value = focusState.isFocused + } + .background(color = PawKeyTheme.colors.background) + .border( + width = 1.dp, + color = borderColor, + shape = RoundedCornerShape(8.dp) + ), + textStyle = PawKeyTheme.typography.bodyActive, + maxLines = if (singleLine) 1 else Int.MAX_VALUE, + singleLine = singleLine, + enabled = enabled, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + cursorBrush = SolidColor(PawKeyTheme.colors.primary), + decorationBox = { innerTextField -> + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Box( + modifier = Modifier + .weight(1f) + ) { + if (value.isEmpty()) { + Text( + text = placeholder, + style = PawKeyTheme.typography.bodyDefault, + color = PawKeyTheme.colors.defaultMiddle + ) + } + innerTextField() + } + suffix?.invoke() + } + } + ) + } +} + +@Preview +@Composable +private fun UserEditTextFieldPreview() { + PawKeyTheme { + UserEditTextField( + value = "", + onValueChange = {}, + placeholder = "이름을 입력해주세요." + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/component/UserGenderButton.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/component/UserGenderButton.kt new file mode 100644 index 00000000..cd7e75a8 --- /dev/null +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/component/UserGenderButton.kt @@ -0,0 +1,75 @@ +package com.paw.key.presentation.ui.mypage.userinfo.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +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.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.paw.key.core.designsystem.theme.PawKeyTheme +import com.paw.key.core.extension.noRippleClickable + +@Composable +fun UserGenderButton( + user: String, + isSelect: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Box( + contentAlignment = Alignment.Center, + modifier = modifier + .clip(RoundedCornerShape(8.dp)) + .background( + color = if (isSelect) { + PawKeyTheme.colors.primary + } else { + PawKeyTheme.colors.background + } + ) + .border( + width = 1.dp, + color = if (isSelect) { + Color.Transparent + } else { + PawKeyTheme.colors.defaultMiddle + }, + shape = RoundedCornerShape(8.dp) + ) + .noRippleClickable(onClick) + .padding(horizontal = 68.dp, vertical = 16.dp) + ) { + Text( + text = user, + color = if (isSelect) { + PawKeyTheme.colors.background + } else { + PawKeyTheme.colors.defaultMiddle + }, + style = if (isSelect) { + PawKeyTheme.typography.bodyActive + } else { + PawKeyTheme.typography.bodyDefault + }, + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun UserGenderButtonPreview() { + PawKeyTheme { + UserGenderButton( + user = "남자", + isSelect = true, + onClick = {} + ) + } +} diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/component/UserProfileItem.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/component/UserProfileItem.kt new file mode 100644 index 00000000..d1333898 --- /dev/null +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/component/UserProfileItem.kt @@ -0,0 +1,47 @@ +package com.paw.key.presentation.ui.mypage.userinfo.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.paw.key.core.designsystem.theme.PawKeyTheme + +@Composable +fun UserProfileItem( + label: String, + profileItem: @Composable () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxWidth() + .background(color = PawKeyTheme.colors.background) + .padding(horizontal = 16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + text = label, + style = PawKeyTheme.typography.bodyActive, + color = PawKeyTheme.colors.contents, + ) + + profileItem() + } +} + +@Preview(showBackground = true) +@Composable +private fun UserProfileItemPreview() { + PawKeyTheme { + UserProfileItem( + label = "Name", + profileItem = {} + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/state/UserProfileContract.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/model/UserProfileContract.kt similarity index 88% rename from app/src/main/java/com/paw/key/presentation/ui/mypage/state/UserProfileContract.kt rename to app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/model/UserProfileContract.kt index d36dac91..310f9234 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/state/UserProfileContract.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/model/UserProfileContract.kt @@ -1,4 +1,4 @@ -package com.paw.key.presentation.ui.mypage.state +package com.paw.key.presentation.ui.mypage.userinfo.model import androidx.compose.runtime.Immutable diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/UserProfileNavigation.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/navigation/UserProfileNavigation.kt similarity index 78% rename from app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/UserProfileNavigation.kt rename to app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/navigation/UserProfileNavigation.kt index bb193df3..863e51dd 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/navigation/UserProfileNavigation.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/navigation/UserProfileNavigation.kt @@ -1,4 +1,4 @@ -package com.paw.key.presentation.ui.mypage.navigation +package com.paw.key.presentation.ui.mypage.userinfo.navigation import androidx.compose.foundation.layout.PaddingValues import androidx.compose.material3.SnackbarHostState @@ -7,11 +7,11 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable import com.paw.key.core.navigation.Route -import com.paw.key.presentation.ui.mypage.UserProfileRoute +import com.paw.key.presentation.ui.mypage.userinfo.UserProfileRoute import kotlinx.serialization.Serializable fun NavController.navigateUserProfile( - navOptions: NavOptions? + navOptions: NavOptions?, ) { navigate(UserProfile, navOptions) } @@ -20,7 +20,7 @@ fun NavGraphBuilder.userProfileNavGraph( paddingValues: PaddingValues, navigateUp: () -> Unit, navigateNext: () -> Unit, - snackBarHostState: SnackbarHostState + snackBarHostState: SnackbarHostState, ) { composable { UserProfileRoute( diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/UserProfileViewModel.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/viewmodel/UserProfileViewModel.kt similarity index 91% rename from app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/UserProfileViewModel.kt rename to app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/viewmodel/UserProfileViewModel.kt index c64ff693..ed1bdf6b 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/UserProfileViewModel.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/mypage/userinfo/viewmodel/UserProfileViewModel.kt @@ -1,12 +1,12 @@ -package com.paw.key.presentation.ui.mypage.viewmodel +package com.paw.key.presentation.ui.mypage.userinfo.viewmodel import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.paw.key.core.util.PreferenceDataStore import com.paw.key.domain.repository.userprofile.UserProfileRepository -import com.paw.key.presentation.ui.mypage.state.UserProfileState -import com.paw.key.presentation.ui.mypage.state.UserProfileSideEffect +import com.paw.key.presentation.ui.mypage.userinfo.model.UserProfileSideEffect +import com.paw.key.presentation.ui.mypage.userinfo.model.UserProfileState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/ArchivedDetailViewModel.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/ArchivedDetailViewModel.kt deleted file mode 100644 index 8b5338f3..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/ArchivedDetailViewModel.kt +++ /dev/null @@ -1,73 +0,0 @@ -package com.paw.key.presentation.ui.mypage.viewmodel - -import android.util.Log -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.paw.key.domain.repository.walklist.WalkListRepository -import com.paw.key.presentation.ui.mypage.state.ArchivedDetailState -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class ArchivedDetailViewModel @Inject constructor( - private val walkListDetailRepository: WalkListRepository -) : ViewModel() { - private val _state = MutableStateFlow(ArchivedDetailState()) - val state: StateFlow - get() = _state.asStateFlow() - - fun onClickImage(imageUrl: String) { - _state.update { - it.copy( - clickImage = imageUrl - ) - } - } - - fun getWalkDetail(userId: Int, postId: Int) { - viewModelScope.launch { - walkListDetailRepository.getWalkListDetail(userId, postId) - .onSuccess { result -> - _state.update { - it.copy( - postTitle = result.title, - petName = result.authorInfo.petName, - createdAt = result.createdAt, - regionName = result.regionName, - categorySummary = result.categoryTags.categoryOptionSummary, - routeMapImageUrl = result.routeMapImageUrl, - walkingImageUrls = result.walkingImageUrls, - postContent = result.content, - petProfileImage = result.authorInfo.petProfileImage, - ) - } - } - .onFailure { - Log.e("getWalkDetail", "getWalkDetail: ${it.message}") - } - } - } - - fun getWalkTopPopular(userId: Int, routeId : Int) { - viewModelScope.launch { - walkListDetailRepository.getWalkTopPopular(userId, routeId) - .onSuccess { result -> - _state.update { - it.copy( - categoryTop3 = result.categoryTop3, - totalReviewCount = result.totalReviewCount - ) - } - Log.d("getWalkTopPopular", "getWalkTopPopular: ${result.categoryTop3}") - } - .onFailure { - Log.e("getWalkTopPopular", "getWalkTopPopular: 실패ㅐㅐ") - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/ArchivedListViewModel.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/ArchivedListViewModel.kt deleted file mode 100644 index f19e3f1d..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/ArchivedListViewModel.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.paw.key.presentation.ui.mypage.viewmodel - -import android.util.Log -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.paw.key.core.designsystem.component.CourseCard -import com.paw.key.domain.repository.ArchivedListRepository -import com.paw.key.domain.repository.SavedListRepository -import com.paw.key.presentation.ui.mypage.state.ArchivedListSideEffect -import com.paw.key.presentation.ui.mypage.state.ArchivedListState -import com.paw.key.presentation.ui.mypage.state.MyPageSideEffect -import com.paw.key.presentation.ui.mypage.state.PetProfileSideEffect.NavigateNext -import com.paw.key.presentation.ui.mypage.state.SavedListSideEffect -import com.paw.key.presentation.ui.mypage.state.SavedListState -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class ArchivedListViewModel @Inject constructor( - private val archivedListRepository: ArchivedListRepository -) : ViewModel() { - - private val _state = MutableStateFlow(ArchivedListState()) - val state: StateFlow = _state.asStateFlow() - - private val _sideEffect = MutableSharedFlow() - val sideEffect: MutableSharedFlow = _sideEffect - - fun getArchivedList(userId: Int) { - viewModelScope.launch { - archivedListRepository.getArchivedList(userId) - .onSuccess { result -> - _state.update { - it.copy( - courseList = result.posts - ) - } - } - .onFailure { e -> - Log.e("ArchivedListViewModel", "저장한 게시물 불러오기 실패", e) - _sideEffect.emit(ArchivedListSideEffect.ShowSnackBar(e.message ?: "알 수 없는 오류")) - } - } - } - fun toggleLike(postId: Int, isLiked: Boolean) { - viewModelScope.launch { - _state.update { state -> - state.copy( - courseList = state.courseList.map { - if (it.postId == postId) it.copy(isLiked = isLiked) else it - } - ) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/SavedDetailViewModel.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/SavedDetailViewModel.kt deleted file mode 100644 index 4afa606a..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/SavedDetailViewModel.kt +++ /dev/null @@ -1,73 +0,0 @@ -package com.paw.key.presentation.ui.mypage.viewmodel - -import android.util.Log -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.paw.key.domain.repository.walklist.WalkListRepository -import com.paw.key.presentation.ui.mypage.state.SavedDetailState -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class SavedDetailViewModel @Inject constructor( - private val walkListDetailRepository: WalkListRepository -) : ViewModel() { - private val _state = MutableStateFlow(SavedDetailState()) - val state: StateFlow - get() = _state.asStateFlow() - - fun onClickImage(imageUrl: String) { - _state.update { - it.copy( - clickImage = imageUrl - ) - } - } - - fun getWalkDetail(userId: Int, postId: Int) { - viewModelScope.launch { - walkListDetailRepository.getWalkListDetail(userId, postId) - .onSuccess { result -> - _state.update { - it.copy( - postTitle = result.title, - petName = result.authorInfo.petName, - createdAt = result.createdAt, - regionName = result.regionName, - categorySummary = result.categoryTags.categoryOptionSummary, - routeMapImageUrl = result.routeMapImageUrl, - walkingImageUrls = result.walkingImageUrls, - postContent = result.content, - petProfileImage = result.authorInfo.petProfileImage, - ) - } - } - .onFailure { - Log.e("getWalkDetail", "getWalkDetail: ${it.message}") - } - } - } - - fun getWalkTopPopular(userId: Int, routeId : Int) { - viewModelScope.launch { - walkListDetailRepository.getWalkTopPopular(userId, routeId) - .onSuccess { result -> - _state.update { - it.copy( - categoryTop3 = result.categoryTop3, - totalReviewCount = result.totalReviewCount - ) - } - Log.d("getWalkTopPopular", "getWalkTopPopular: ${result.categoryTop3}") - } - .onFailure { - Log.e("getWalkTopPopular", "getWalkTopPopular: 실패ㅐㅐ") - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/SavedListViewModel.kt b/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/SavedListViewModel.kt deleted file mode 100644 index d0b5a030..00000000 --- a/app/src/main/java/com/paw/key/presentation/ui/mypage/viewmodel/SavedListViewModel.kt +++ /dev/null @@ -1,132 +0,0 @@ -package com.paw.key.presentation.ui.mypage.viewmodel - -import android.util.Log -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.paw.key.core.util.PreferenceDataStore -import com.paw.key.domain.repository.LikeRepository -import com.paw.key.domain.repository.SavedListRepository -import com.paw.key.presentation.ui.mypage.state.SavedListSideEffect -import com.paw.key.presentation.ui.mypage.state.SavedListState -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class SavedListViewModel @Inject constructor( - private val savedListRepository: SavedListRepository, - private val likeRepository: LikeRepository -) : ViewModel() { - - private val _state = MutableStateFlow(SavedListState()) - val state: StateFlow = _state.asStateFlow() - - private val _sideEffect = MutableSharedFlow() - val sideEffect: MutableSharedFlow = _sideEffect - - private val userId = PreferenceDataStore.getUserId() - - init { - getSavedList() - } - - fun getSavedList() { - viewModelScope.launch { - savedListRepository.getSavedList(userId.first()) - .onSuccess { result -> - _state.update { - it.copy( - courseList = result.posts - ) - } - } - .onFailure { e -> - Log.e("SavedListViewModel", "저장한 게시물 불러오기 실패", e) - _sideEffect.emit(SavedListSideEffect.ShowSnackBar(e.message ?: "알 수 없는 오류")) - } - } - } - -// fun toggleLike(postId: Int, isLiked: Boolean) { -// viewModelScope.launch { -// _state.update { state -> -// state.copy( -// courseList = state.courseList.map { -// if (it.postId == postId) it.copy(isLiked = isLiked) else it -// } -// ) -// } -// } -// } - - fun toggleLike( - postId: Int, - isLiked: Boolean - ) { - // Todo : userId 네비게이션 연결, postId 네비게이션 연결 -// viewModelScope.launch { -// likeRepository.unlikeCourse(userId = 2, postId = postId) -// savedListRepository.getSavedList(userId = 2) -// } - viewModelScope.launch { - likeRepository.likeCourse(userId = 2, postId = 6) - savedListRepository.getSavedList(userId = 2) - } - } -} - -//@HiltViewModel -//class SavedListViewModel @Inject constructor( -// private val savedListRepository: SavedListRepository, -// private val likeRepository: LikeRepository -//) : ViewModel() { -// -// private val _state = MutableStateFlow(SavedListState()) -// val state: StateFlow = _state.asStateFlow() -// -// private val _sideEffect = MutableSharedFlow() -// val sideEffect: MutableSharedFlow = _sideEffect -// -// // ✅ 테스트용 고정 유저 ID -// private val testUserId = 2 -// -// init { -// getSavedList(testUserId) -// } -// -// // ✅ 하드코딩된 userId 사용 -// fun getSavedList(userId: Int) { -// viewModelScope.launch { -// savedListRepository.getSavedList(userId) -// .onSuccess { result -> -// _state.update { -// it.copy(courseList = result.posts) -// } -// } -// .onFailure { e -> -// Log.e("SavedListViewModel", "저장한 게시물 불러오기 실패", e) -// _sideEffect.emit(SavedListSideEffect.ShowSnackBar(e.message ?: "알 수 없는 오류")) -// } -// } -// } -// -// // ✅ 테스트용 좋아요 토글 -// fun toggleLike(postId: Int, isLiked: Boolean) { -// viewModelScope.launch { -// if (isLiked) { -// likeRepository.unlikeCourse(userId = testUserId, postId = postId) -// } else { -// likeRepository.likeCourse(userId = testUserId, postId = postId) -// } -// -// // 갱신 -// getSavedList(testUserId) -// } -// } -//} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mypage_app_inf.xml b/app/src/main/res/drawable/ic_mypage_app_inf.xml new file mode 100644 index 00000000..010ca3c7 --- /dev/null +++ b/app/src/main/res/drawable/ic_mypage_app_inf.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_mypage_arrow_right.xml b/app/src/main/res/drawable/ic_mypage_arrow_right.xml new file mode 100644 index 00000000..e939fc7e --- /dev/null +++ b/app/src/main/res/drawable/ic_mypage_arrow_right.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_mypage_delete.xml b/app/src/main/res/drawable/ic_mypage_delete.xml new file mode 100644 index 00000000..65e18d14 --- /dev/null +++ b/app/src/main/res/drawable/ic_mypage_delete.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_mypage_logout.xml b/app/src/main/res/drawable/ic_mypage_logout.xml new file mode 100644 index 00000000..1919e3b8 --- /dev/null +++ b/app/src/main/res/drawable/ic_mypage_logout.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_mypage_mypost.xml b/app/src/main/res/drawable/ic_mypage_mypost.xml new file mode 100644 index 00000000..7047d9ce --- /dev/null +++ b/app/src/main/res/drawable/ic_mypage_mypost.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_range_setting.xml b/app/src/main/res/drawable/ic_range_setting.xml new file mode 100644 index 00000000..e8a4cde8 --- /dev/null +++ b/app/src/main/res/drawable/ic_range_setting.xml @@ -0,0 +1,9 @@ + + +