diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index dc0efb69..765315af 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -10,6 +10,9 @@
+
+
+
Unit,
modifier: Modifier = Modifier
) {
Column(
@@ -53,6 +56,9 @@ fun CourseCard(
.fillMaxWidth()
.size(width = 328.dp , height = 240.dp)
.background(Color.White, shape = RoundedCornerShape(20.dp))
+ .noRippleClickable {
+ onCLickItem()
+ }
) {
// 지도 썸네일
Box(
@@ -168,6 +174,7 @@ fun CourseCardPreview() {
title = "홍대 주변 좋은 산책 코스",
petName = "반려견 이름",
date = "2025/05/17",
+ onCLickItem = {}
)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/paw/key/core/designsystem/component/CourseDetail.kt b/app/src/main/java/com/paw/key/core/designsystem/component/CourseDetail.kt
index 4f605264..49d329e9 100644
--- a/app/src/main/java/com/paw/key/core/designsystem/component/CourseDetail.kt
+++ b/app/src/main/java/com/paw/key/core/designsystem/component/CourseDetail.kt
@@ -53,28 +53,6 @@ fun CourseDetail(
modifier = modifier
.fillMaxWidth()
) {
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(vertical = 16.dp),
- verticalAlignment = Alignment.CenterVertically
- ) {
- Icon(
- imageVector = ImageVector.vectorResource(R.drawable.ic_arrow_left_black),
- contentDescription = "뒤로가기"
- )
- Box(
- modifier = Modifier.weight(1f),
- contentAlignment = Alignment.Center
- ) {
- Text(
- text = "저장한 산책 루트",
- style = PawKeyTheme.typography.body16Sb
- )
- }
- Spacer(modifier = Modifier.width(24.dp))
- }
-
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data("https://pawkey-server.com/image.jpg")
diff --git a/app/src/main/java/com/paw/key/data/di/RepositoryModule.kt b/app/src/main/java/com/paw/key/data/di/RepositoryModule.kt
index 4cec4372..abbd3e91 100644
--- a/app/src/main/java/com/paw/key/data/di/RepositoryModule.kt
+++ b/app/src/main/java/com/paw/key/data/di/RepositoryModule.kt
@@ -1,8 +1,10 @@
package com.paw.key.data.di
import com.paw.key.data.repositoryimpl.DummyRepositoryImpl
+import com.paw.key.data.repositoryimpl.RegionRepositoryImpl
import com.paw.key.data.repositoryimpl.WalkSharedResultRepositoryImpl
import com.paw.key.domain.repository.DummyRepository
+import com.paw.key.domain.repository.RegionRepository
import com.paw.key.domain.repository.WalkSharedResultRepository
import dagger.Binds
import dagger.Module
@@ -25,4 +27,10 @@ interface RepositoryModule {
walkSharedResultRepositoryImpl: WalkSharedResultRepositoryImpl
): WalkSharedResultRepository
+ /*Home*/
+ @Binds
+ @Singleton
+ fun bindsRegionRepository(
+ regionRepositoryImpl: RegionRepositoryImpl
+ ): RegionRepository
}
\ No newline at end of file
diff --git a/app/src/main/java/com/paw/key/data/di/ServiceModule.kt b/app/src/main/java/com/paw/key/data/di/ServiceModule.kt
index e97134b3..7fd0552c 100644
--- a/app/src/main/java/com/paw/key/data/di/ServiceModule.kt
+++ b/app/src/main/java/com/paw/key/data/di/ServiceModule.kt
@@ -1,6 +1,7 @@
package com.paw.key.data.di
import com.paw.key.data.service.DummyService
+import com.paw.key.data.service.RegionService
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -17,4 +18,10 @@ object ServiceModule {
fun providesDummyService(retrofit: Retrofit ): DummyService =
retrofit.create(DummyService::class.java)
+ @Provides
+ @Singleton
+ fun providesRegionService(retrofit: Retrofit ): RegionService =
+ retrofit.create(RegionService::class.java)
+
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/paw/key/data/dto/response/BaseResponse.kt b/app/src/main/java/com/paw/key/data/dto/response/BaseResponse.kt
index b5fdc03b..54f43a44 100644
--- a/app/src/main/java/com/paw/key/data/dto/response/BaseResponse.kt
+++ b/app/src/main/java/com/paw/key/data/dto/response/BaseResponse.kt
@@ -6,7 +6,7 @@ import kotlinx.serialization.Serializable
@Serializable
data class BaseResponse(
@SerialName("code")
- val code: Int,
+ val code: String,
@SerialName("message")
val message: String,
@SerialName("data")
diff --git a/app/src/main/java/com/paw/key/data/dto/response/region/RegionResponseDto.kt b/app/src/main/java/com/paw/key/data/dto/response/region/RegionResponseDto.kt
new file mode 100644
index 00000000..82777fa8
--- /dev/null
+++ b/app/src/main/java/com/paw/key/data/dto/response/region/RegionResponseDto.kt
@@ -0,0 +1,20 @@
+package com.paw.key.data.dto.response.region
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class RegionResponseDto(
+ @SerialName("regionName")
+ val regionName: String,
+ @SerialName("geometryDto")
+ val geometryDto: GeometryDto
+)
+
+@Serializable
+data class GeometryDto(
+ @SerialName("type")
+ val type: String,
+ @SerialName("coordinates")
+ val coordinates: List>>>
+)
diff --git a/app/src/main/java/com/paw/key/data/mapper/RegionMapper.kt b/app/src/main/java/com/paw/key/data/mapper/RegionMapper.kt
new file mode 100644
index 00000000..a1e8c971
--- /dev/null
+++ b/app/src/main/java/com/paw/key/data/mapper/RegionMapper.kt
@@ -0,0 +1,30 @@
+package com.paw.key.data.mapper
+
+import com.paw.key.data.dto.response.region.GeometryDto
+import com.paw.key.data.dto.response.region.RegionResponseDto
+import com.paw.key.domain.model.entity.region.GeometryEntity
+import com.paw.key.domain.model.entity.region.RegionDataEntity
+import javax.inject.Inject
+
+class RegionMapper @Inject constructor() {
+ fun mapDtoToEntity(dto: RegionResponseDto): RegionDataEntity {
+ return RegionDataEntity(
+ regionName = dto.regionName,
+ geometry = dto.geometryDto.toEntity()
+ )
+ }
+
+ private fun GeometryDto.toEntity(): GeometryEntity {
+ return GeometryEntity(
+ type = this.type,
+ coordinates = this.coordinates.map { polygon ->
+ polygon.map { ring ->
+ ring.map { point ->
+ // 서버에서 위도 경도 다름
+ Pair(point[1], point[0])
+ }
+ }
+ }
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/paw/key/data/remote/datasource/RegionDataSource.kt b/app/src/main/java/com/paw/key/data/remote/datasource/RegionDataSource.kt
new file mode 100644
index 00000000..fa39eae0
--- /dev/null
+++ b/app/src/main/java/com/paw/key/data/remote/datasource/RegionDataSource.kt
@@ -0,0 +1,10 @@
+package com.paw.key.data.remote.datasource
+
+import com.paw.key.data.service.RegionService
+import javax.inject.Inject
+
+class RegionDataSource @Inject constructor (
+ private val regionService: RegionService
+) {
+ suspend fun getRegionGeometry(userId: Int, regionId: Int) = regionService.getRegionGeometry(userId, regionId)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/paw/key/data/repositoryimpl/RegionRepositoryImpl.kt b/app/src/main/java/com/paw/key/data/repositoryimpl/RegionRepositoryImpl.kt
new file mode 100644
index 00000000..05e973a6
--- /dev/null
+++ b/app/src/main/java/com/paw/key/data/repositoryimpl/RegionRepositoryImpl.kt
@@ -0,0 +1,19 @@
+package com.paw.key.data.repositoryimpl
+
+import com.paw.key.data.mapper.RegionMapper
+import com.paw.key.data.remote.datasource.RegionDataSource
+import com.paw.key.domain.model.entity.region.RegionDataEntity
+import com.paw.key.domain.repository.RegionRepository
+import javax.inject.Inject
+
+
+class RegionRepositoryImpl @Inject constructor(
+ private val regionDataSource: RegionDataSource,
+ private val mapper: RegionMapper
+) : RegionRepository {
+ override suspend fun getRegionGeometry(userId: Int, regionId: Int): Result = runCatching {
+ regionDataSource.getRegionGeometry(userId, regionId).data.let {
+ mapper.mapDtoToEntity(it)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/paw/key/data/service/RegionService.kt b/app/src/main/java/com/paw/key/data/service/RegionService.kt
new file mode 100644
index 00000000..e262de63
--- /dev/null
+++ b/app/src/main/java/com/paw/key/data/service/RegionService.kt
@@ -0,0 +1,15 @@
+package com.paw.key.data.service
+
+import com.paw.key.data.dto.response.BaseResponse
+import com.paw.key.data.dto.response.region.RegionResponseDto
+import retrofit2.http.GET
+import retrofit2.http.Header
+import retrofit2.http.Path
+
+interface RegionService {
+ @GET("regions/{regionId}/geometry")
+ suspend fun getRegionGeometry(
+ @Header("X-USER-ID") userId: Int,
+ @Path("regionId") regionId: Int,
+ ): BaseResponse
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/paw/key/domain/model/entity/region/RegionEntity.kt b/app/src/main/java/com/paw/key/domain/model/entity/region/RegionEntity.kt
index 4d51299a..932640e9 100644
--- a/app/src/main/java/com/paw/key/domain/model/entity/region/RegionEntity.kt
+++ b/app/src/main/java/com/paw/key/domain/model/entity/region/RegionEntity.kt
@@ -1,19 +1,11 @@
package com.paw.key.domain.model.entity.region
-import com.kakao.vectormap.LatLng
-
-data class RegionResponse(
- val code: String,
- val message: String,
- val data: RegionData
-)
-
-data class RegionData(
+data class RegionDataEntity(
val regionName: String,
- val geometryDto: GeometryDto
+ val geometry: GeometryEntity
)
-data class GeometryDto(
+data class GeometryEntity(
val type: String,
val coordinates: List>>> // MultiPolygon은 여러 폴리곤의 리스트를 가짐
)
diff --git a/app/src/main/java/com/paw/key/domain/repository/RegionRepository.kt b/app/src/main/java/com/paw/key/domain/repository/RegionRepository.kt
new file mode 100644
index 00000000..05a06930
--- /dev/null
+++ b/app/src/main/java/com/paw/key/domain/repository/RegionRepository.kt
@@ -0,0 +1,7 @@
+package com.paw.key.domain.repository
+
+import com.paw.key.domain.model.entity.region.RegionDataEntity
+
+interface RegionRepository {
+ suspend fun getRegionGeometry(userId: Int, regionId: Int): Result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/TabListScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/TabListScreen.kt
index 5b0198e5..5347944c 100644
--- a/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/TabListScreen.kt
+++ b/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/TabListScreen.kt
@@ -98,28 +98,32 @@ fun TabListScreen(
CourseCard(
title = "제목을 입력해주세요",
petName = "안녕꼬리",
- date = "21/1/1"
+ date = "21/1/1",
+ onCLickItem = {}
)
}
item {
CourseCard(
title = "제목을 입력해주세요",
petName = "안녕꼬리",
- date = "21/1/1"
+ date = "21/1/1",
+ onCLickItem = {}
)
}
item {
CourseCard(
title = "제목을 입력해주세요",
petName = "안녕꼬리",
- date = "21/1/1"
+ date = "21/1/1",
+ onCLickItem = {}
)
}
item {
CourseCard(
title = "제목을 입력해주세요",
petName = "안녕꼬리",
- date = "21/1/1"
+ date = "21/1/1",
+ onCLickItem = {}
)
}
}
diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/WalkReviewScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/WalkReviewScreen.kt
index 72b07049..611e7368 100644
--- a/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/WalkReviewScreen.kt
+++ b/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/WalkReviewScreen.kt
@@ -1,5 +1,6 @@
package com.paw.key.presentation.ui.course.walkreview
+import android.Manifest
import android.net.Uri
import android.os.Build
import androidx.activity.compose.rememberLauncherForActivityResult
@@ -56,6 +57,12 @@ fun WalkReviewRoute(
val lifecycleOwner = LocalLifecycleOwner.current
+ val imagePermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Manifest.permission.READ_MEDIA_IMAGES
+ } else {
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ }
+
val pickMultipleMediaLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.PickMultipleVisualMedia(5)
) { uris ->
@@ -64,6 +71,24 @@ fun WalkReviewRoute(
}
}
+ val galleryLauncher = rememberLauncherForActivityResult(
+ ActivityResultContracts.GetMultipleContents()
+ ) { uris: List ->
+ if (uris.isNotEmpty()) {
+ val limitedUris = uris.take(5)
+ viewModel.onImagesSelected(limitedUris)
+ }
+ }
+
+ val permissionLauncher = rememberLauncherForActivityResult(
+ ActivityResultContracts.RequestPermission()
+ ) { isGranted ->
+ if (isGranted) {
+ galleryLauncher.launch("image/*")
+ }
+ }
+
+
LaunchedEffect(viewModel.sideEffect, lifecycleOwner) {
viewModel.sideEffect.flowWithLifecycle(lifecycleOwner.lifecycle)
.collect { sideEffect ->
@@ -110,9 +135,13 @@ fun WalkReviewRoute(
viewModel.onContentTextChanged(it)
},
onClickImage = {
- pickMultipleMediaLauncher.launch(
- PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageAndVideo)
- )
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ pickMultipleMediaLauncher.launch(
+ PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageAndVideo)
+ )
+ } else {
+ permissionLauncher.launch(imagePermission)
+ }
},
onImageDelete = {
viewModel.onImageDelete(it)
diff --git a/app/src/main/java/com/paw/key/presentation/ui/home/HomeLocationSettingScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/home/HomeLocationSettingScreen.kt
index e0182d09..11c6ff0c 100644
--- a/app/src/main/java/com/paw/key/presentation/ui/home/HomeLocationSettingScreen.kt
+++ b/app/src/main/java/com/paw/key/presentation/ui/home/HomeLocationSettingScreen.kt
@@ -147,7 +147,7 @@ fun HomeLocationSettingScreen(
enabled = isFormValid,
onClick = {
if (isFormValid) {
-
+ navigateNext()
}
}
)
diff --git a/app/src/main/java/com/paw/key/presentation/ui/home/HomeScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/home/HomeScreen.kt
index 0ac6119d..460f3f9a 100644
--- a/app/src/main/java/com/paw/key/presentation/ui/home/HomeScreen.kt
+++ b/app/src/main/java/com/paw/key/presentation/ui/home/HomeScreen.kt
@@ -159,6 +159,7 @@ fun HomeScreen(
title = "제목을 입력해주세요",
petName = "반려견 이름",
date = "년도/월/일",
+ onCLickItem = {}
)
}
item{}
@@ -179,19 +180,15 @@ fun HomeScreen(
) {
viewModel.toggleLocationMenu()
}
- )
-
- Box(
- contentAlignment = Alignment.TopEnd,
- modifier = Modifier
- .padding(top = 97.dp, start = 240.dp),
) {
SettingButton(
modifier = Modifier
+ .padding(top = 43.dp, end = 16.dp)
.noRippleClickable {
viewModel.toggleLocationMenu()
navigateHomeLocationSetting()
- },
+ }
+ .align(Alignment.TopEnd),
)
}
}
diff --git a/app/src/main/java/com/paw/key/presentation/ui/login/LoginScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/login/LoginScreen.kt
index 81562acb..5d6431b1 100644
--- a/app/src/main/java/com/paw/key/presentation/ui/login/LoginScreen.kt
+++ b/app/src/main/java/com/paw/key/presentation/ui/login/LoginScreen.kt
@@ -138,7 +138,7 @@ fun LoginScreen(
onClick = navigateUp,
enabled = true,
isBackGround = true,
- isBorder = true,
+ isBorder = false,
modifier = Modifier
.fillMaxWidth()
)
@@ -154,6 +154,6 @@ fun LoginScreen(
.navigationBarsPadding()
)
- Spacer(modifier = Modifier.height(12.dp))
+ Spacer(modifier = Modifier.height(60.dp))
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/paw/key/presentation/ui/main/MainScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/main/MainScreen.kt
index 0f9df9d0..37d1f3c9 100644
--- a/app/src/main/java/com/paw/key/presentation/ui/main/MainScreen.kt
+++ b/app/src/main/java/com/paw/key/presentation/ui/main/MainScreen.kt
@@ -1,6 +1,9 @@
package com.paw.key.presentation.ui.main
+import android.app.Activity
import android.os.Build
+import android.widget.Toast
+import androidx.activity.compose.BackHandler
import androidx.annotation.RequiresApi
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box
@@ -15,10 +18,14 @@ import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableLongStateOf
+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.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalContext
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.paw.key.presentation.animation.FootprintAnimationScreen
@@ -50,8 +57,27 @@ fun MainScreen(
removeFootprint: (MainContract.Footprint) -> Unit,
navigator: MainNavigator = rememberMainNavigator(),
) {
+ val context = LocalContext.current
val snackBarHostState = remember { SnackbarHostState() }
+ val toast = remember {
+ Toast.makeText(context, "한 번 더 누르면 종료합니다.", Toast.LENGTH_SHORT)
+ }
+
+ var backPressedTime by remember { mutableLongStateOf(0L) }
+
+ if (navigator.currentTab != null) {
+ BackHandler {
+ val currentTime = System.currentTimeMillis()
+ if (currentTime - backPressedTime <= 2000L) {
+ (context as? Activity)?.finishAffinity()
+ } else {
+ backPressedTime = currentTime
+ toast.show()
+ }
+ }
+ }
+
MainScreenContent(
navigator = navigator,
snackBarHostState = snackBarHostState,
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 d34fc402..b495b801 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
@@ -62,7 +62,7 @@ fun PawKeyNavHost(
homeLocationSettingNavGraph(
paddingValues = paddingValues,
navigateUp = navigator::navigateUp,
- navigateNext = navigator::navigateCourse,
+ navigateNext = navigator::navigateRegional,
navigateHomeLocationSetting = navigator::navigateHomeLocationSetting,
modifier = modifier,
)
@@ -123,8 +123,10 @@ fun PawKeyNavHost(
navigateNext = navigator::navigateArchivedDetail,
modifier = modifier
)
+
savedDetailNavGraph(
navigateUp = navigator::navigateUp,
+ navigateToWalk = navigator::navigateWalkCourse,
snackBarHostState = snackbarHostState
)
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
index 30037f9e..45f99d08 100644
--- 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
@@ -68,6 +68,7 @@ fun ArchivedCourseListScreen(
title = course.title,
petName = course.petName,
date = course.date,
+ onCLickItem = navigateNext
)
}
}
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
index 9a9fafb1..c366b26e 100644
--- 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
@@ -3,16 +3,20 @@ package com.paw.key.presentation.ui.mypage
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.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -26,10 +30,12 @@ import com.paw.key.core.designsystem.theme.White1
@Composable
fun SavedDetailRoute(
navigateUp: () -> Unit,
+ navigateToWalk: () -> Unit,
modifier: Modifier = Modifier,
) {
SavedCourseDetailScreen(
navigateUp = navigateUp,
+ navigateToWalk = navigateToWalk,
modifier = modifier
)
}
@@ -37,41 +43,70 @@ fun SavedDetailRoute(
@Composable
fun SavedCourseDetailScreen(
navigateUp: () -> Unit,
+ navigateToWalk: () -> Unit,
modifier: Modifier = Modifier
-){
+) {
var isImageExpanded by remember { mutableStateOf(false) }
- Box(modifier = Modifier.fillMaxSize()) {
- TopBar(title = "내가 저장한 산책 루트",
- onBackClick = { navigateUp() }
+ Column(modifier = Modifier.fillMaxSize()) {
+ TopBar(
+ title = "내가 저장한 산책 루트",
+ onBackClick = navigateUp
)
- LazyColumn(
- modifier = modifier
- .fillMaxWidth()
- .padding(16.dp)
- .background(color = White1)
+ Box(
+ modifier = Modifier.weight(1f)
) {
- item {
- CourseDetail(
- title = "한강 산책로",
- petName = "후추",
- date = "2025/06/02",
- location = "뚝섬유원지",
- distance = "4.5km",
- option = listOf("풍경이 좋아요", "조용해요", "길이 깨끗해요"),
- time = "1시간 30분 소요",
- onImageClick = { isImageExpanded = true } // ← 콜백 전달
- )
- PawkeyButton(
- text = "해당 루트로 산책하기",
- enabled = true,
- onClick = {},
- modifier = Modifier
- .fillMaxWidth()
- .padding(top = 16.dp)
- )
+ LazyColumn(
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(16.dp)
+ .background(color = White1)
+ ) {
+ item {
+ CourseDetail(
+ title = "한강 산책로",
+ petName = "후추",
+ date = "2025/06/02",
+ location = "뚝섬유원지",
+ distance = "4.5km",
+ option = listOf("풍경이 좋아요", "조용해요", "길이 깨끗해요"),
+ time = "1시간 30분 소요",
+ onImageClick = {
+ isImageExpanded = true
+ }
+ )
+
+ }
+
+ item {
+ Spacer(modifier = Modifier.height(16.dp))
+
+ HorizontalDivider(
+ thickness = 8.dp,
+ color = PawKeyTheme.colors.gray50,
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(bottom = 8.dp)
+ )
+
+ Spacer(modifier = Modifier.height(120.dp))
+ }
}
+
+
+
+ PawkeyButton(
+ text = "해당 루트로 산책하기",
+ enabled = true,
+ onClick = {
+ navigateToWalk()
+ },
+ modifier = Modifier
+ .fillMaxWidth()
+ .align(Alignment.BottomCenter)
+ .padding(top = 24.dp, start = 16.dp, end = 16.dp, bottom = 60.dp)
+ )
}
if (isImageExpanded) {
@@ -83,10 +118,14 @@ fun SavedCourseDetailScreen(
}
}
+
@Preview
@Composable
fun SavedCourseDetailPreview(){
PawKeyTheme {
- SavedCourseDetailScreen(navigateUp = {})
+ SavedCourseDetailScreen(
+ navigateUp = {},
+ navigateToWalk = {}
+ )
}
}
\ No newline at end of file
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
index 7f1b6906..6a10275c 100644
--- 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
@@ -80,9 +80,7 @@ fun SavedCourseListScreen(
title = item.title,
petName = item.petName,
date = item.date,
- modifier = Modifier.clickable {
- navigateNext()
- }
+ onCLickItem = navigateNext
)
}
}
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
index 2d701bf0..0ed95387 100644
--- 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
@@ -17,7 +17,7 @@ import kotlinx.serialization.Serializable
fun NavController.navigateArchivedDetail(
navOptions: NavOptions?
) {
- navigate(SavedCourse, navOptions)
+ navigate(ArchivedDetail, navOptions)
}
fun NavGraphBuilder.archivedDetailNavGraph(
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
index 834e70a2..09263024 100644
--- 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
@@ -16,17 +16,19 @@ import kotlinx.serialization.Serializable
fun NavController.navigateSavedDetail(
navOptions: NavOptions?
) {
- navigate(SavedCourse, navOptions)
+ navigate(SavedDetail, navOptions)
}
fun NavGraphBuilder.savedDetailNavGraph(
navigateUp: () -> Unit,
+ navigateToWalk : () -> Unit,
snackBarHostState: SnackbarHostState,
modifier: Modifier = Modifier,
) {
composable {
SavedDetailRoute(
navigateUp = navigateUp,
+ navigateToWalk = navigateToWalk,
modifier = modifier
)
}
diff --git a/app/src/main/java/com/paw/key/presentation/ui/onboard/OnboardingScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/onboard/OnboardingScreen.kt
index 019002da..1805fe09 100644
--- a/app/src/main/java/com/paw/key/presentation/ui/onboard/OnboardingScreen.kt
+++ b/app/src/main/java/com/paw/key/presentation/ui/onboard/OnboardingScreen.kt
@@ -86,14 +86,15 @@ fun OnboardingScreen(
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
PawkeyButton(
- text = "로그인",
+ text = "신규 계정으로 회원가입",
enabled = true,
- onClick = { navigateSignUp() },
+ onClick = { },
)
PawkeyButton(
- text = "회원가입",
- enabled = false,
+ text = "기존 계정으로 로그인",
+ enabled = true,
+ isBackGround = true,
onClick = { navigateSignUp() },
)
}
diff --git a/app/src/main/java/com/paw/key/presentation/ui/region/RegionalManagementScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/region/RegionalManagementScreen.kt
index 9ca76e06..46bdeff7 100644
--- a/app/src/main/java/com/paw/key/presentation/ui/region/RegionalManagementScreen.kt
+++ b/app/src/main/java/com/paw/key/presentation/ui/region/RegionalManagementScreen.kt
@@ -34,6 +34,7 @@ import androidx.lifecycle.flowWithLifecycle
import com.kakao.vectormap.LatLng
import com.kakao.vectormap.MapView
import com.paw.key.core.designsystem.component.CustomSnackBar
+import com.paw.key.core.designsystem.component.PawkeyButton
import com.paw.key.core.designsystem.theme.PawKeyTheme
import com.paw.key.core.util.UiState
import com.paw.key.presentation.ui.region.component.regionalMapView
@@ -56,7 +57,7 @@ fun RegionalManagementRoute(
LaunchedEffect(Unit) {
// Todo : 나중에 서버에서 좌표받아올때 변경
- val polyPoints: List> = listOf(
+ /*val polyPoints: List> = listOf(
// 첫 번째 폴리곤 (JSON의 첫 번째 MultiPolygon 내부 배열)
listOf(
LatLng.from(37.51711061445719, 127.02190765991858),
@@ -194,9 +195,12 @@ fun RegionalManagementRoute(
LatLng.from(37.52700302845431, 127.00996246776681),
LatLng.from(37.52701147918971, 127.00997077242218) // 닫는 좌표
)
- )
+ )*/
- viewModel.getRegionPoints(polyPoints)
+ viewModel.getRegionGeometry(
+ X_USER_ID = 2,
+ regionId = 35,
+ )
}
LaunchedEffect(viewModel.sideEffect, lifecycleOwner) {
@@ -218,7 +222,7 @@ fun RegionalManagementRoute(
val mapView = regionalMapView(
lifeCycle = lifecycleOwner.lifecycle,
context = context,
- currentUserLocation = LatLng.from(37.497942, 127.027619),
+ currentUserLocation = state.centerLocation,
polyPoints = (state.uiState as UiState.Success>>).data
)
@@ -326,24 +330,15 @@ fun RegionalManagementScreen(
Spacer(modifier = Modifier.height(12.dp))
- Button(
+ PawkeyButton(
+ text = "지역 변경하기",
onClick = {
onClickButton()
},
modifier = Modifier
- .fillMaxWidth()
- .height(52.dp),
- shape = RoundedCornerShape(8.dp),
- colors = ButtonDefaults.buttonColors(
- containerColor = if (selectedRegion != null) PawKeyTheme.colors.green500 else PawKeyTheme.colors.gray500
- )
- ) {
- Text(
- text = "지역 변경하기",
- style = PawKeyTheme.typography.body16Sb,
- color = PawKeyTheme.colors.white1
- )
- }
+ .fillMaxWidth(),
+ enabled = true,
+ )
}
}
}
diff --git a/app/src/main/java/com/paw/key/presentation/ui/region/state/RegionContract.kt b/app/src/main/java/com/paw/key/presentation/ui/region/state/RegionContract.kt
index 3fe56031..f2a090b5 100644
--- a/app/src/main/java/com/paw/key/presentation/ui/region/state/RegionContract.kt
+++ b/app/src/main/java/com/paw/key/presentation/ui/region/state/RegionContract.kt
@@ -8,8 +8,8 @@ class RegionContract {
@Immutable
data class RegionState(
val uiState: UiState>> = UiState.Loading,
- val selectedRegion : String? = null,
- val centerLocation : LatLng? = null,
+ val selectedRegion: String? = null,
+ val centerLocation: LatLng? = null,
)
sealed class RegionSideEffect {
diff --git a/app/src/main/java/com/paw/key/presentation/ui/region/viewmodel/RegionViewModel.kt b/app/src/main/java/com/paw/key/presentation/ui/region/viewmodel/RegionViewModel.kt
index fb27cc50..f88725ac 100644
--- a/app/src/main/java/com/paw/key/presentation/ui/region/viewmodel/RegionViewModel.kt
+++ b/app/src/main/java/com/paw/key/presentation/ui/region/viewmodel/RegionViewModel.kt
@@ -1,24 +1,25 @@
package com.paw.key.presentation.ui.region.viewmodel
+import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.kakao.vectormap.LatLng
import com.paw.key.core.util.UiState
-import com.paw.key.domain.model.entity.region.GeometryDto
+import com.paw.key.core.util.handleError
+import com.paw.key.domain.repository.RegionRepository
import com.paw.key.presentation.ui.region.state.RegionContract
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class RegionViewModel @Inject constructor(
-
+ private val regionRepository: RegionRepository
) : ViewModel() {
private val _state = MutableStateFlow(RegionContract.RegionState())
val state : StateFlow
@@ -28,12 +29,56 @@ class RegionViewModel @Inject constructor(
val sideEffect : MutableSharedFlow
get() = _sideEffect
- fun getRegionPoints(points : List>) {
- viewModelScope.launch {
- _state.value = _state.value.copy(
- uiState = UiState.Success(points)
- )
- }
+ fun getRegionGeometry(X_USER_ID: Int, regionId: Int) = viewModelScope.launch {
+ regionRepository.getRegionGeometry(X_USER_ID, regionId)
+ .onSuccess { data ->
+ Log.d("RegionViewModel", "API 응답 성공: $data")
+ Log.d("RegionViewModel", "geometry type: ${data.geometry.type}")
+ Log.d("RegionViewModel", "coordinates size: ${data.geometry.coordinates.size}")
+
+ val coordinates = data.geometry.coordinates
+ val flattenedLatLng = flattenCoordinatesToLatLng(coordinates)
+
+ Log.d("RegionViewModel", "flattenedLatLng size: ${flattenedLatLng}")
+
+ _state.update {
+ it.copy(
+ uiState = UiState.Success(flattenedLatLng)
+ )
+ }
+
+ val firstPoint = coordinates
+ .firstOrNull() // 첫 번째 Polygon
+ ?.firstOrNull() // 첫 번째 Ring (외부 경계)
+ ?.firstOrNull() // 첫 번째 Point
+
+ Log.d("RegionViewModel", "First point: $firstPoint")
+
+ if (firstPoint != null) {
+ val latLng = LatLng.from(firstPoint.first, firstPoint.second)
+ _state.update {
+ it.copy(
+ centerLocation = latLng,
+ selectedRegion = data.regionName
+ )
+ }
+ } else {
+ _state.update {
+ it.copy(
+ uiState = UiState.Failure("좌표 데이터가 올바르지 않습니다")
+ )
+ }
+ }
+ }
+ .onFailure { throwable ->
+ Log.e("RegionViewModel", "API 호출 실패", throwable)
+ val errorMessage = handleError(throwable)
+ _state.update {
+ it.copy(
+ uiState = UiState.Failure(errorMessage)
+ )
+ }
+ }
}
fun onChangeRegion() {
@@ -43,18 +88,14 @@ class RegionViewModel @Inject constructor(
)
}
}
+}
- fun GeometryDto.toLatLngList(): List {
- val latLngList = mutableListOf()
- this.coordinates.forEach { polygon ->
- polygon.forEach { linearRing ->
- linearRing.forEach { coordinate ->
- latLngList.add(
- LatLng.from(coordinate.second, coordinate.first)
- )
- }
- }
- }
- return latLngList
+private fun flattenCoordinatesToLatLng(
+ coordinates: List>>>
+): List> {
+ return coordinates.map { polygon -> // 각 Polygon
+ polygon.firstOrNull()?.map { point ->
+ LatLng.from(point.first, point.second)
+ }.orEmpty()
}
}
\ No newline at end of file