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 87dcdce4..9736d201 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 @@ -7,6 +7,8 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight @@ -48,6 +50,7 @@ import com.paw.key.core.util.noRippleClickable import com.paw.key.domain.model.entity.walklist.CategoryTop3Entity import kotlinx.serialization.json.JsonNull.content +@OptIn(ExperimentalLayoutApi::class) @Composable fun CourseDetail( title : String, @@ -164,8 +167,9 @@ fun CourseDetail( .build(), contentDescription = null, modifier = Modifier - .size(44.dp) - .clip(CircleShape) + .size(48.dp) + .clip(CircleShape), + contentScale = ContentScale.Crop ) Text( @@ -207,9 +211,10 @@ fun CourseDetail( } // 카테고리 칩들 - Row( + FlowRow ( horizontalArrangement = Arrangement.spacedBy(8.dp), - modifier = Modifier.padding(vertical = 13.dp) + modifier = Modifier.padding(vertical = 13.dp), + maxItemsInEachRow = 3, ) { categorySummary.forEach { category -> SubChip(text = category) diff --git a/app/src/main/java/com/paw/key/core/designsystem/component/ImageModal.kt b/app/src/main/java/com/paw/key/core/designsystem/component/ImageModal.kt index 0d8accaa..7f402f94 100644 --- a/app/src/main/java/com/paw/key/core/designsystem/component/ImageModal.kt +++ b/app/src/main/java/com/paw/key/core/designsystem/component/ImageModal.kt @@ -3,6 +3,7 @@ package com.paw.key.core.designsystem.component import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentSize @@ -41,6 +42,8 @@ fun ImageModal( model = imageUrl, contentDescription = null, modifier = Modifier + .fillMaxWidth() + .padding(16.dp) ) // AsyncImage( diff --git a/app/src/main/java/com/paw/key/core/designsystem/component/PageIndicator.kt b/app/src/main/java/com/paw/key/core/designsystem/component/PageIndicator.kt new file mode 100644 index 00000000..3aae5bc1 --- /dev/null +++ b/app/src/main/java/com/paw/key/core/designsystem/component/PageIndicator.kt @@ -0,0 +1,130 @@ +package com.paw.key.core.designsystem.component + +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.CornerRadius +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.paw.key.core.designsystem.theme.PawKeyTheme + +@Composable +fun PageIndicator( + numberOfPages: Int, + modifier: Modifier = Modifier, + selectedPage: Int = 0, + selectedColor: Color, + defaultColor: Color, + defaultRadius: Dp = 8.dp, + selectedLength: Dp = 24.dp, + space: Dp = 12.dp, + animationDurationInMillis: Int = 300, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(space), + modifier = modifier, + ) { + for (i in 0 until numberOfPages) { + val isSelected = i == selectedPage + PageIndicatorView( + isSelected = isSelected, + selectedColor = selectedColor, + defaultColor = defaultColor, + defaultRadius = defaultRadius, + selectedLength = selectedLength, + animationDurationInMillis = animationDurationInMillis, + ) + } + } +} + +@Composable +fun PageIndicatorView( + isSelected: Boolean, + selectedColor: Color, + defaultColor: Color, + defaultRadius: Dp, + selectedLength: Dp, + animationDurationInMillis: Int, + modifier: Modifier = Modifier, +) { + + val color: Color by animateColorAsState( + targetValue = if (isSelected) { + selectedColor + } else { + defaultColor + }, + animationSpec = tween( + durationMillis = animationDurationInMillis, + ) + ) + val width: Dp by animateDpAsState( + targetValue = if (isSelected) { + selectedLength + } else { + defaultRadius + }, + animationSpec = tween( + durationMillis = animationDurationInMillis, + ) + ) + + Canvas( + modifier = modifier + .size( + width = width, + height = defaultRadius, + ), + ) { + drawRoundRect( + color = color, + topLeft = Offset.Zero, + size = Size( + width = width.toPx(), + height = defaultRadius.toPx(), + ), + cornerRadius = CornerRadius( + x = defaultRadius.toPx(), + y = defaultRadius.toPx(), + ), + ) + } +} + +@Preview +@Composable +private fun IndicatorPreview() { + PawKeyTheme { + val page = rememberPagerState( + pageCount = { 4 } + ) + PageIndicator( + numberOfPages = page.pageCount, + selectedPage = page.currentPage, + defaultRadius = 6.dp, // ← 원 기본 크기 + selectedLength = 16.dp, // ← 선택된 인디케이터 길이 + space = 8.dp, // ← 인디케이터 간 간격 + animationDurationInMillis = 300, + selectedColor = PawKeyTheme.colors.green500, + defaultColor = PawKeyTheme.colors.green200, + modifier = Modifier + .padding(bottom = 20.dp) + ) + } +} diff --git a/app/src/main/java/com/paw/key/core/util/NoRippleInteractionSource.kt b/app/src/main/java/com/paw/key/core/util/NoRippleInteractionSource.kt new file mode 100644 index 00000000..7f437920 --- /dev/null +++ b/app/src/main/java/com/paw/key/core/util/NoRippleInteractionSource.kt @@ -0,0 +1,13 @@ +package com.paw.key.core.util + +import androidx.compose.foundation.interaction.Interaction +import androidx.compose.foundation.interaction.MutableInteractionSource +import kotlinx.coroutines.flow.emptyFlow + +class NoRippleInteractionSource : MutableInteractionSource { + override val interactions = emptyFlow() + + override suspend fun emit(interaction: Interaction) { } + + override fun tryEmit(interaction: Interaction): Boolean = true +} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/data/dto/request/walkreview/WalkCourseReviewRequestDto.kt b/app/src/main/java/com/paw/key/data/dto/request/walkreview/WalkCourseReviewRequestDto.kt index f2641d74..9c832d8a 100644 --- a/app/src/main/java/com/paw/key/data/dto/request/walkreview/WalkCourseReviewRequestDto.kt +++ b/app/src/main/java/com/paw/key/data/dto/request/walkreview/WalkCourseReviewRequestDto.kt @@ -15,7 +15,10 @@ data class WalkCourseReviewRequestDto( @SerialName("isPublic") val isPublic: Boolean, - @SerialName("selectedCategories") + @SerialName("isMine") + val isMine: Boolean, + + @SerialName("selectedOptionsForCategories") val selectedCategories: List, @SerialName("routeId") diff --git a/app/src/main/java/com/paw/key/data/dto/response/filter/FilterOptionResponse.kt b/app/src/main/java/com/paw/key/data/dto/response/filter/FilterOptionResponse.kt index 6fcf1585..56c2240b 100644 --- a/app/src/main/java/com/paw/key/data/dto/response/filter/FilterOptionResponse.kt +++ b/app/src/main/java/com/paw/key/data/dto/response/filter/FilterOptionResponse.kt @@ -1,23 +1,61 @@ package com.paw.key.data.dto.response.filter +import com.paw.key.domain.model.entity.filter.Category +import com.paw.key.domain.model.entity.filter.CategoryOption +import com.paw.key.domain.model.entity.filter.FilterEntity +import com.paw.key.domain.model.entity.filter.SelectOption +import com.paw.key.domain.model.entity.filter.SelectOptionItem +import com.paw.key.domain.model.entity.walklist.CategoryTagsEntity import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -@Serializable -data class FilterOptionResponseDto( - @SerialName("code") - val code: String, - @SerialName("message") - val message: String, - @SerialName("data") - val data: FilterOptionResponse -) - @Serializable data class FilterOptionResponse( + @SerialName("selectList") + val selectList: List, @SerialName("categoryList") val categoryList: List -) +) { + fun toEntity(): FilterEntity { + return FilterEntity( + selectList = selectList.map { it.toEntity() }, + categoryList = categoryList.map { it.toEntity() } + ) + } +} + +@Serializable +data class SelectDto( + @SerialName("selectId") + val selectId: Int? = null, + @SerialName("selectName") + val selectName: String? = null, + @SerialName("options") + val options: List? = null +) { + fun toEntity(): SelectOption { + return SelectOption( + selectId = selectId ?: 0, + selectName = selectName ?: "", + options = options?.map { it.toEntity() } ?: emptyList(), + ) + } +} + +@Serializable +data class OptionDto( + @SerialName("selectOptionId") + val selectOptionId: Int? = null, + @SerialName("selectText") + val selectText: String? = null +) { + fun toEntity(): SelectOptionItem { + return SelectOptionItem( + selectOptionId = selectOptionId ?: 0, + selectText = selectText ?: "" + ) + } +} @Serializable data class CategoryDto( @@ -27,14 +65,30 @@ data class CategoryDto( val categoryDescription: String? = null, @SerialName("categoryName") val categoryName: String? = null, - @SerialName("categoryOptions") + @SerialName("options") val categoryOptions: List? = null -) + ) { + fun toEntity(): Category { + return Category( + categoryId = categoryId ?: 0, + categoryName = categoryName ?: "", + categoryDescription = categoryDescription ?: "", + categoryOptions = categoryOptions?.map { it.toEntity() } ?: emptyList() + ) + } +} @Serializable data class CategoryOptionDto( @SerialName("categoryOptionId") val categoryOptionId: Int? = null, - @SerialName("categoryOptionText") + @SerialName("optionText") val categoryOptionText: String? = null -) \ No newline at end of file +) { + fun toEntity(): CategoryOption { + return CategoryOption( + categoryOptionId = categoryOptionId ?: 0, + categoryOptionText = categoryOptionText ?: "" + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/data/dto/response/list/PostsListResponseDto.kt b/app/src/main/java/com/paw/key/data/dto/response/list/PostsListResponseDto.kt index af3f09ca..a6b8985a 100644 --- a/app/src/main/java/com/paw/key/data/dto/response/list/PostsListResponseDto.kt +++ b/app/src/main/java/com/paw/key/data/dto/response/list/PostsListResponseDto.kt @@ -22,6 +22,10 @@ data class PostDto ( val isLike : Boolean, @SerialName("title") val title : String, + @SerialName("isMine") + val isMine : Boolean, + @SerialName("isPublic") + val isPublic : Boolean, @SerialName("representativeImageUrl") val representativeImageUrl : String, @SerialName("routeId") @@ -56,7 +60,9 @@ fun PostDto.toEntity(): PostEntity { title = title, representativeImageUrl = representativeImageUrl, routeId = routeId, + isMine = isMine, writer = writer.toEntity(), + isPublic = isPublic, descriptionTags = descriptionTags ) } diff --git a/app/src/main/java/com/paw/key/data/dto/response/walkreview/WalkReviewResponseDto.kt b/app/src/main/java/com/paw/key/data/dto/response/walkreview/WalkReviewResponseDto.kt new file mode 100644 index 00000000..e5a8ba32 --- /dev/null +++ b/app/src/main/java/com/paw/key/data/dto/response/walkreview/WalkReviewResponseDto.kt @@ -0,0 +1,17 @@ +package com.paw.key.data.dto.response.walkreview + +import com.paw.key.domain.model.entity.walkreview.WalkReviewIdEntity +import kotlinx.serialization.Serializable + +@Serializable +data class WalkReviewResponseDto( + val postId: Int, + val routeId : Int +) { + fun toEntity(): WalkReviewIdEntity { + return WalkReviewIdEntity( + postId = postId, + routeId = routeId + ) + } +} diff --git a/app/src/main/java/com/paw/key/data/remote/datasource/filter/FilterOptionDataSource.kt b/app/src/main/java/com/paw/key/data/remote/datasource/filter/FilterOptionDataSource.kt index a45d6dc6..96cd4561 100644 --- a/app/src/main/java/com/paw/key/data/remote/datasource/filter/FilterOptionDataSource.kt +++ b/app/src/main/java/com/paw/key/data/remote/datasource/filter/FilterOptionDataSource.kt @@ -9,7 +9,5 @@ import javax.inject.Inject class FilterOptionDataSource @Inject constructor( private val filterOptionService: FilterOptionService ) { - suspend fun getFilterOptions(userId: Int): BaseResponse { - return filterOptionService.getFilterOptions(userId) - } + suspend fun getFilterOptions(userId: Int) = filterOptionService.getFilterOptions(userId).data } diff --git a/app/src/main/java/com/paw/key/data/remote/datasource/walkreview/WalkReviewDataSource.kt b/app/src/main/java/com/paw/key/data/remote/datasource/walkreview/WalkReviewDataSource.kt index 04e2bb55..a9c8574b 100644 --- a/app/src/main/java/com/paw/key/data/remote/datasource/walkreview/WalkReviewDataSource.kt +++ b/app/src/main/java/com/paw/key/data/remote/datasource/walkreview/WalkReviewDataSource.kt @@ -3,6 +3,7 @@ package com.paw.key.data.remote.datasource.walkreview import com.paw.key.data.dto.request.walkcourse.WalkCourseRequestDto import com.paw.key.data.dto.request.walkreview.WalkCourseReviewRequestDto import com.paw.key.data.dto.response.BaseResponse +import com.paw.key.data.dto.response.walkreview.WalkReviewResponseDto import com.paw.key.data.service.walkreview.WalkReviewService import kotlinx.serialization.json.Json import okhttp3.MediaType.Companion.toMediaType @@ -17,7 +18,7 @@ class WalkReviewDataSource @Inject constructor( userId: Int, imageFiles: List, walkReviewRequestDto: WalkCourseReviewRequestDto - ) : BaseResponse { + ) : BaseResponse { val jsonString = Json.encodeToString(WalkCourseReviewRequestDto.serializer(), walkReviewRequestDto) val requestBody = jsonString.toRequestBody("application/json".toMediaType()) diff --git a/app/src/main/java/com/paw/key/data/repositoryimpl/filter/FilterOptionRepositoryImpl.kt b/app/src/main/java/com/paw/key/data/repositoryimpl/filter/FilterOptionRepositoryImpl.kt index f4123089..5b825ac2 100644 --- a/app/src/main/java/com/paw/key/data/repositoryimpl/filter/FilterOptionRepositoryImpl.kt +++ b/app/src/main/java/com/paw/key/data/repositoryimpl/filter/FilterOptionRepositoryImpl.kt @@ -11,41 +11,7 @@ class FilterOptionRepositoryImpl @Inject constructor( private val dataSource: FilterOptionDataSource ) : FilterOptionRepository { - override suspend fun getFilterOptions(userId: Int): Result { - return try { - val response = dataSource.getFilterOptions(userId) - - if (response.code == "S000") { - val filterEntity = FilterEntity( - categoryList = try { - response.data.categoryList.map { categoryDto -> - Category( - categoryId = categoryDto.categoryId ?: 0, - categoryName = categoryDto.categoryName ?: "", - categoryOptions = categoryDto.categoryOptions?.map { optionDto -> - CategoryOption( - categoryOptionId = optionDto.categoryOptionId ?: 0, - categoryOptionText = optionDto.categoryOptionText ?: "" - ) - } ?: emptyList() - ) - } - } catch (e: Exception) { - e.printStackTrace() - emptyList() - } - ) - - filterEntity.categoryList?.forEach { category -> - } - - Result.success(filterEntity) - } else { - Result.failure(Exception("API Error: ${response.message}")) - } - } catch (e: Exception) { - e.printStackTrace() - Result.failure(e) - } + override suspend fun getFilterOptions(userId: Int): Result = runCatching { + dataSource.getFilterOptions(userId).toEntity() } } \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/data/repositoryimpl/list/PostsListRepositoryImpl.kt b/app/src/main/java/com/paw/key/data/repositoryimpl/list/PostsListRepositoryImpl.kt index 3a61afda..32401584 100644 --- a/app/src/main/java/com/paw/key/data/repositoryimpl/list/PostsListRepositoryImpl.kt +++ b/app/src/main/java/com/paw/key/data/repositoryimpl/list/PostsListRepositoryImpl.kt @@ -10,12 +10,11 @@ import javax.inject.Inject class PostsListRepositoryImpl @Inject constructor( private val dataSource: PostsListDataSource, ) : PostsListRepository { - override suspend fun postList(userId: Int, request: PostsListRequestDto) : Result = runCatching { val response = dataSource.postList(userId, request) if (response.code == "S000") { - response.data?.toEntity() ?: throw Exception("Data is null") + response.data.toEntity() } else { throw Exception(response.message) } @@ -24,7 +23,7 @@ class PostsListRepositoryImpl @Inject constructor( override suspend fun getAllPosts(userId: Int): Result = runCatching { val response = dataSource.getAllPosts(userId) if (response.code == "S000") { - response.data?.toEntity() ?: throw Exception("Data is null") + response.data.toEntity() ?: throw Exception("Data is null") } else { throw Exception(response.message) } diff --git a/app/src/main/java/com/paw/key/data/repositoryimpl/walkreview/WalkReviewRepositoryImpl.kt b/app/src/main/java/com/paw/key/data/repositoryimpl/walkreview/WalkReviewRepositoryImpl.kt index 61c1e985..746647b6 100644 --- a/app/src/main/java/com/paw/key/data/repositoryimpl/walkreview/WalkReviewRepositoryImpl.kt +++ b/app/src/main/java/com/paw/key/data/repositoryimpl/walkreview/WalkReviewRepositoryImpl.kt @@ -2,6 +2,7 @@ package com.paw.key.data.repositoryimpl.walkreview import com.paw.key.data.remote.datasource.walkreview.WalkReviewDataSource import com.paw.key.domain.model.entity.walkreview.WalkReviewCategoryListEntity +import com.paw.key.domain.model.entity.walkreview.WalkReviewIdEntity import com.paw.key.domain.model.entity.walkreview.WalkReviewInfoEntity import com.paw.key.domain.model.entity.walkreview.WalkReviewRecordEntity import com.paw.key.domain.repository.walkreview.WalkReviewRepository @@ -15,13 +16,13 @@ class WalkReviewRepositoryImpl @Inject constructor( userId: Int, imageFiles: List, walkReviewRequest: WalkReviewRecordEntity - ): Result { + ): Result { return runCatching { dataSource.postWalkReview( userId = userId, imageFiles = imageFiles, walkReviewRequestDto = walkReviewRequest.toDto() - ) + ).data.toEntity() } } diff --git a/app/src/main/java/com/paw/key/data/service/filter/FilterOptionService.kt b/app/src/main/java/com/paw/key/data/service/filter/FilterOptionService.kt index f88a4714..40469b82 100644 --- a/app/src/main/java/com/paw/key/data/service/filter/FilterOptionService.kt +++ b/app/src/main/java/com/paw/key/data/service/filter/FilterOptionService.kt @@ -7,7 +7,7 @@ import retrofit2.http.Header interface FilterOptionService { - @GET ("posts/categories") + @GET ("posts/filter") suspend fun getFilterOptions( @Header("X-USER-ID") userId: Int, ): BaseResponse diff --git a/app/src/main/java/com/paw/key/data/service/walkreview/WalkReviewService.kt b/app/src/main/java/com/paw/key/data/service/walkreview/WalkReviewService.kt index 135bc1e2..68df59ca 100644 --- a/app/src/main/java/com/paw/key/data/service/walkreview/WalkReviewService.kt +++ b/app/src/main/java/com/paw/key/data/service/walkreview/WalkReviewService.kt @@ -3,6 +3,7 @@ package com.paw.key.data.service.walkreview import com.paw.key.data.dto.response.BaseResponse import com.paw.key.data.dto.response.walkreview.WalkReviewCategoryResponseDto import com.paw.key.data.dto.response.walkreview.WalkReviewInfoResponseDto +import com.paw.key.data.dto.response.walkreview.WalkReviewResponseDto import okhttp3.MultipartBody import okhttp3.RequestBody import retrofit2.http.GET @@ -19,7 +20,7 @@ interface WalkReviewService { @Header("X-USER-ID") userId: Int, @Part imageFiles: List, @Part("data") data: RequestBody - ): BaseResponse + ): BaseResponse @GET("posts/categories") suspend fun getWalkReviewCategory( diff --git a/app/src/main/java/com/paw/key/domain/model/entity/list/ListEntity.kt b/app/src/main/java/com/paw/key/domain/model/entity/list/ListEntity.kt index 9651cbcf..d3a6e1fc 100644 --- a/app/src/main/java/com/paw/key/domain/model/entity/list/ListEntity.kt +++ b/app/src/main/java/com/paw/key/domain/model/entity/list/ListEntity.kt @@ -8,6 +8,8 @@ data class PostEntity( val postId: Int, val createdAt: String, val isLike: Boolean, + val isMine: Boolean, + val isPublic: Boolean, val title: String, val representativeImageUrl: String, val routeId: Int, diff --git a/app/src/main/java/com/paw/key/domain/model/entity/walkreview/WalkReviewIdEntity.kt b/app/src/main/java/com/paw/key/domain/model/entity/walkreview/WalkReviewIdEntity.kt new file mode 100644 index 00000000..0b7044aa --- /dev/null +++ b/app/src/main/java/com/paw/key/domain/model/entity/walkreview/WalkReviewIdEntity.kt @@ -0,0 +1,6 @@ +package com.paw.key.domain.model.entity.walkreview + +data class WalkReviewIdEntity( + val postId: Int, + val routeId : Int +) diff --git a/app/src/main/java/com/paw/key/domain/model/entity/walkreview/WalkReviewRecordEntity.kt b/app/src/main/java/com/paw/key/domain/model/entity/walkreview/WalkReviewRecordEntity.kt index 9acc606d..a5866769 100644 --- a/app/src/main/java/com/paw/key/domain/model/entity/walkreview/WalkReviewRecordEntity.kt +++ b/app/src/main/java/com/paw/key/domain/model/entity/walkreview/WalkReviewRecordEntity.kt @@ -7,6 +7,7 @@ data class WalkReviewRecordEntity( val title: String, val description: String, val isPublic: Boolean, + val isMine: Boolean, val categories: List, val routeId: Long ) { @@ -15,6 +16,7 @@ data class WalkReviewRecordEntity( title = title, description = description, isPublic = isPublic, + isMine = isMine, selectedCategories = categories.map { category -> SelectedCategoryDto( categoryId = category.categoryId, diff --git a/app/src/main/java/com/paw/key/domain/repository/walkreview/WalkReviewRepository.kt b/app/src/main/java/com/paw/key/domain/repository/walkreview/WalkReviewRepository.kt index 71d06531..f0e7553b 100644 --- a/app/src/main/java/com/paw/key/domain/repository/walkreview/WalkReviewRepository.kt +++ b/app/src/main/java/com/paw/key/domain/repository/walkreview/WalkReviewRepository.kt @@ -3,6 +3,7 @@ package com.paw.key.domain.repository.walkreview import com.paw.key.data.dto.request.walkreview.WalkCourseReviewRequestDto import com.paw.key.data.dto.response.walkcourse.WalkCourseResponseDto import com.paw.key.domain.model.entity.walkreview.WalkReviewCategoryListEntity +import com.paw.key.domain.model.entity.walkreview.WalkReviewIdEntity import com.paw.key.domain.model.entity.walkreview.WalkReviewInfoEntity import com.paw.key.domain.model.entity.walkreview.WalkReviewRecordEntity import okhttp3.MultipartBody @@ -12,7 +13,7 @@ interface WalkReviewRepository { userId: Int, imageFiles: List, walkReviewRequest: WalkReviewRecordEntity - ) : Result + ) : Result suspend fun getWalkReviewInfo( userId: Int, diff --git a/app/src/main/java/com/paw/key/presentation/ui/community/CommunityScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/community/CommunityScreen.kt index cb6118e0..57c1f4f5 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/community/CommunityScreen.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/community/CommunityScreen.kt @@ -1,5 +1,6 @@ package com.paw.key.presentation.ui.community +import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -9,9 +10,12 @@ 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.vector.ImageVector import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import com.paw.key.R +import com.paw.key.core.designsystem.theme.PawKeyTheme @Composable fun CommunityRoute( @@ -44,9 +48,16 @@ fun CommunityScreen( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ){ + Image( + imageVector = ImageVector.vectorResource(R.drawable.ic_community), + contentDescription = stringResource(R.string.ic_community_description), + ) + Text( - text = stringResource(R.string.ic_community_description), - modifier = modifier + text = "커뮤니티 기능은\n 아직 준비중이에요", + modifier = modifier, + style = PawKeyTheme.typography.body16Sb, + color = PawKeyTheme.colors.gray300 ) } } diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/entire/EntireCourseScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/course/entire/EntireCourseScreen.kt index c825d907..86e3638f 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/entire/EntireCourseScreen.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/entire/EntireCourseScreen.kt @@ -4,6 +4,7 @@ import android.Manifest import android.content.Context import android.content.pm.PackageManager import android.os.Build +import android.util.Log import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.RequiresApi @@ -39,7 +40,7 @@ fun EntireCourseRoute( paddingValues: PaddingValues, navigateUp: () -> Unit, navigateNext: () -> Unit, - navigateToDetail: () -> Unit, + navigateToDetail: (Int, Int) -> Unit, routeIndex: Int, snackBarHostState: SnackbarHostState, setOnVisibleRecord: (Boolean) -> Unit, @@ -137,7 +138,9 @@ fun EntireCourseRoute( } setOnVisibleRecord(it) }, - navigateToDetail = navigateToDetail, + navigateToDetail = { postId, routeId -> + navigateToDetail(postId, routeId) + }, isGranted = state.isLocationPermissionGranted, tabs = state.courseTabs, modifier = modifier, @@ -154,7 +157,7 @@ fun EntireCourseScreen( currentPage : Int, navigateUp: () -> Unit, navigateNext: () -> Unit, - navigateToDetail : () -> Unit, + navigateToDetail : (Int, Int) -> Unit, setOnVisibleRecord : (Boolean) -> Unit, onTabSelected : (Int) -> Unit, modifier: Modifier = Modifier, @@ -178,29 +181,23 @@ fun EntireCourseScreen( when (currentPage) { 0 -> { - if (!isGranted) { - TapMapRoute( - paddingValues = paddingValues, - navigateUp = {}, - navigateNext = { - navigateNext() - }, - isGranted = isGranted, - snackBarHostState = snackBarHostState, - ) - } else { - // 스낵바 알림만 띄우고 아무 것도 렌더링하지 않음 - LaunchedEffect(Unit) { - scope.launch { - snackBarHostState.showSnackbar("위치 권한이 필요합니다.") - } - } - } + TapMapRoute( + paddingValues = paddingValues, + navigateUp = {}, + navigateNext = { + navigateNext() + }, + isGranted = isGranted, + snackBarHostState = snackBarHostState, + ) } 1 -> { TapListRoute( - navigateToDetail = navigateToDetail + navigateToDetail = { postId, routeId -> + Log.e("TabListScreen", "postId: $postId, routeId: $routeId") + navigateToDetail(postId, routeId) + } ) } } @@ -246,7 +243,8 @@ private fun EntireCourseScreenPreview() { onTabSelected = {}, isGranted = true, setOnVisibleRecord = {}, - navigateToDetail = {}, + navigateToDetail = { postId, routeId -> + }, modifier = Modifier, ) } diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/entire/component/EntireCourseTabRow.kt b/app/src/main/java/com/paw/key/presentation/ui/course/entire/component/EntireCourseTabRow.kt index 77fa4042..0dc5db92 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/entire/component/EntireCourseTabRow.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/entire/component/EntireCourseTabRow.kt @@ -1,5 +1,6 @@ package com.paw.key.presentation.ui.course.entire.component +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.height import androidx.compose.material3.ScrollableTabRow import androidx.compose.material3.Tab @@ -7,11 +8,13 @@ import androidx.compose.material3.TabRowDefaults import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.paw.key.core.designsystem.theme.PawKeyTheme +import com.paw.key.core.util.NoRippleInteractionSource import com.paw.key.presentation.ui.course.entire.state.EntireCourseContract.CourseTab @Composable @@ -68,6 +71,7 @@ fun EntireCourseTabRow( onClick = { onTabSelected(index) }, + interactionSource = remember { NoRippleInteractionSource() }, ) } } diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/entire/navigation/CourseNavigation.kt b/app/src/main/java/com/paw/key/presentation/ui/course/entire/navigation/CourseNavigation.kt index 234f2eda..7259a667 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/entire/navigation/CourseNavigation.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/entire/navigation/CourseNavigation.kt @@ -24,7 +24,7 @@ fun NavGraphBuilder.courseNavGraph( paddingValues: PaddingValues, navigateUp: () -> Unit, navigateNext: () -> Unit, - navigateToDetail: () -> Unit, + navigateToDetail: (Int, Int) -> Unit, setOnVisibleRecord: (Boolean) -> Unit, snackBarHostState: SnackbarHostState, ) { @@ -36,7 +36,9 @@ fun NavGraphBuilder.courseNavGraph( paddingValues = paddingValues, navigateUp = navigateUp, navigateNext = navigateNext, - navigateToDetail = navigateToDetail, + navigateToDetail = { postId, routeId -> + navigateToDetail(postId, routeId) + }, routeIndex = receivedIndex, setOnVisibleRecord = setOnVisibleRecord, snackBarHostState = snackBarHostState, diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/CourseOptionBottomSheet.kt b/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/CourseOptionBottomSheet.kt index 6deca1f1..291c0202 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/CourseOptionBottomSheet.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/CourseOptionBottomSheet.kt @@ -1,5 +1,6 @@ package com.paw.key.presentation.ui.course.entire.tab.map.List +import android.util.Log import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -250,6 +251,8 @@ private fun SuccessContent( ) { val filterOptions = listState.filterOptions!! + val timeOptions = filterOptions.selectList?.find { it.selectName == "산책 소요 시간" } + Log.e("timeoptions", timeOptions.toString()) val moodOptions = filterOptions.categoryList?.find { it.categoryName == "분위기" } val dogFriendOptions = filterOptions.categoryList?.find { it.categoryName == "강아지 친구" } val safetyOptions = filterOptions.categoryList?.find { it.categoryName == "안전" } @@ -278,6 +281,35 @@ private fun SuccessContent( ) } + timeOptions?.let { selectList -> + item { + CourseOptionToggle( + title = selectList.selectName, + selecttitle = listState.selectedSortTime, + isExpanded = listState.isTimeExpanded, + onClick = { viewModel.toggleTimeExpanded() } + ) + } + + if (listState.isTimeExpanded) { + val options = filterOptions.selectList.flatMap { + it.options ?: emptyList() + } + + items(options) { option -> + SingleOptionItem( + title = option.selectText, + isSelected = listState.selectedSortTime == option.selectText, + onSelect = { viewModel.updateSortTime(option.selectText) } + ) + } + } + + item { + HorizontalDivider(color = PawKeyTheme.colors.gray50, thickness = 1.dp) + } + } + moodOptions?.let { mood -> item { CourseOptionToggle( @@ -290,6 +322,7 @@ private fun SuccessContent( if (listState.isMoodExpanded) { val options = mood.categoryOptions ?: emptyList() + items(options) { option -> SingleOptionItem( title = option.categoryOptionText, @@ -439,14 +472,13 @@ private fun BottomButtons( viewModel.applyOptions() onDismissRequest() }, - modifier = Modifier.width(300.dp) + modifier = Modifier + .weight(0.7f) ) - Spacer(modifier = Modifier.weight(1F)) - IconButton( onClick = { viewModel.resetAllOptions() }, - modifier = Modifier.size(56.dp) + modifier = Modifier.size(56.dp).weight(0.3f) ) { Icon( imageVector = ImageVector.vectorResource(R.drawable.ic_course_list_refresh), 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 952581c0..74284162 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 @@ -3,14 +3,26 @@ package com.paw.key.presentation.ui.course.entire.tab.map.List import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* +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.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.Text -import androidx.compose.runtime.* +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.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -30,45 +42,42 @@ import com.paw.key.presentation.ui.course.entire.tab.map.List.viewmodel.TapListV @Composable private fun PreviewTabListScreen() { PawKeyTheme { - TabListScreen( - navigateToDetail = {}, - onClickLike = { _, _ -> } - ) } } @Composable fun TapListRoute( - navigateToDetail: () -> Unit, + navigateToDetail: (Int, Int) -> Unit, modifier: Modifier = Modifier, viewModel: TapListViewModel = hiltViewModel(), ) { + LaunchedEffect(Unit) { + viewModel.loadInitialPosts() + viewModel.loadFilterOptions() + } + TabListScreen( modifier = modifier, - navigateToDetail = navigateToDetail, - viewModel = viewModel, - onClickLike = { - postId, isLiked -> - viewModel.toggleLike( - userId = 2, - postId = postId - ) - } + navigateToDetail = { postId, routeId -> + navigateToDetail(postId, routeId) + }, + viewModel = viewModel ) } @Composable fun TabListScreen( - navigateToDetail: () -> Unit, + navigateToDetail: (Int, Int) -> Unit, modifier: Modifier = Modifier, viewModel: TapListViewModel = hiltViewModel(), - onClickLike: (postId: Int, isLiked: Boolean) -> Unit ) { var showBottomSheet by remember { mutableStateOf(false) } val listState by viewModel.state.collectAsStateWithLifecycle() + val filterValid = listState.isValid Column( - modifier = modifier.fillMaxSize() + modifier = modifier + .fillMaxSize() ) { Row( horizontalArrangement = Arrangement.spacedBy(8.dp), @@ -82,14 +91,37 @@ fun TabListScreen( imageVector = ImageVector.vectorResource(R.drawable.ic_course_optin_filter), contentDescription = "filter", tint = Color.Unspecified, - modifier = Modifier.noRippleClickable { - showBottomSheet = true - } - ) - OptionChip( - text = if (viewModel.isFilterApplied()) "필터 적용됨" else "선택한 옵션이 없어요", - isActionChip = true + modifier = Modifier + .noRippleClickable { + showBottomSheet = true + } ) + + if (filterValid) { + if (listState.selectedSortTime.isNotEmpty()) { + OptionChip(text = listState.selectedSortTime) + } + if (listState.selectedMood.isNotEmpty()) { + OptionChip(text = listState.selectedMood) + } + if (listState.selectedDogFriend.isNotEmpty()) { + OptionChip(text = listState.selectedDogFriend) + } + listState.selectedSafety.forEach { + OptionChip(text = it) + } + listState.selectedConvenience.forEach { + OptionChip(text = it) + } + listState.selectedEnvironment.forEach { + OptionChip(text = it) + } + } else { + OptionChip( + text = "선택한 옵션이 없어요", + isActionChip = true + ) + } } LazyColumn( @@ -117,27 +149,21 @@ fun TabListScreen( if (posts.isNotEmpty()) { items(posts) { post -> CourseCard( - postId = post.postId, title = post.title, petName = post.writer.petName, - createdAt = post.createdAt, - isLiked = post.isLike, representativeImageUrl = post.representativeImageUrl, petProfileImageUrl = post.writer.petProfileImageUrl, descriptionTags = post.descriptionTags, - onClickLike = { isLiked -> - viewModel.toggleLike( - userId = 2, - postId = post.postId - ) - onClickLike( - post.postId, - isLiked - - ) - + postId = post.postId, + createdAt = post.createdAt, + isLiked = post.isLike, + onClickItem = { + //showBottomSheet = true + navigateToDetail(post.postId, post.routeId) }, - onClickItem = { navigateToDetail() } + onClickLike = { + //viewModel.toggleLike(postId = post.postId) + } ) } } else { diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/state/TapListContract.kt b/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/state/TapListContract.kt index 647af22c..adb53f18 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/state/TapListContract.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/state/TapListContract.kt @@ -2,16 +2,22 @@ package com.paw.key.presentation.ui.course.entire.tab.map.List.state import androidx.compose.runtime.Immutable import com.paw.key.domain.model.entity.filter.FilterEntity +import com.paw.key.domain.model.entity.filter.SelectOption import com.paw.key.domain.model.entity.list.ListEntity class TapListContract { @Immutable data class TapListState( val isLoading: Boolean = false, + val filterTopOptions : SelectOption? = null, val filterOptions: FilterEntity? = null, val postsResult: ListEntity? = null, val selectedSortOption: String = "", + + val selectedSortTimeStart : Int? = 0, + val selectedSortTimeEnd : Int? = 0, + val selectedSortTime : String = "", val selectedMood: String = "", val selectedDogFriend: String = "", @@ -19,12 +25,21 @@ class TapListContract { val selectedConvenience: List = emptyList(), val selectedEnvironment: List = emptyList(), + val isTimeExpanded: Boolean = false, val isMoodExpanded: Boolean = false, val isDogFriendExpanded: Boolean = false, val isSafetyExpanded: Boolean = false, val isConvenienceExpanded: Boolean = false, val isEnvironmentExpanded: Boolean = false, - ) + ) { + val isValid: Boolean + get() = selectedSortTime.isNotEmpty() + || selectedMood.isNotEmpty() + || selectedDogFriend.isNotEmpty() + || selectedSafety.isNotEmpty() + || selectedConvenience.isNotEmpty() + || selectedEnvironment.isNotEmpty() + } object Options { val sortOptions = listOf( diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/viewmodel/TapListViewModel.kt b/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/viewmodel/TapListViewModel.kt index 942e1f93..2b9ff6a0 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/viewmodel/TapListViewModel.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/viewmodel/TapListViewModel.kt @@ -1,7 +1,9 @@ package com.paw.key.presentation.ui.course.entire.tab.map.List.viewmodel +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.paw.key.core.util.PreferenceDataStore import com.paw.key.data.dto.request.list.PostsListRequestDto import com.paw.key.data.dto.request.list.TraitList import com.paw.key.domain.repository.LikeRepository @@ -12,6 +14,7 @@ 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.first import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject @@ -22,16 +25,12 @@ class TapListViewModel @Inject constructor( private val postsListRepository: PostsListRepository, private val likeRepository: LikeRepository ) : ViewModel() { - private val _state = MutableStateFlow(TapListContract.TapListState()) val state: StateFlow = _state.asStateFlow() - init { - loadFilterOptions() - loadInitialPosts() - } + private val userId = PreferenceDataStore.getUserId() - private fun loadInitialPosts() { + fun loadInitialPosts() { viewModelScope.launch { _state.update { it.copy(isLoading = true) } println("POST 요청 시작 - 초기 데이터 로딩 (null 값들)") @@ -51,14 +50,16 @@ class TapListViewModel @Inject constructor( println("요청 데이터: $request") postsListRepository.postList( - userId = 2, + userId = userId.first(), request = request ).onSuccess { listEntity -> - println("응답 성공 - posts 개수: ${listEntity.posts.size}") + val filteredPosts = listEntity.posts.filter { !it.isMine } + println("응답 성공 - 내 게시물 제외된 posts 개수: ${filteredPosts.size}") + _state.update { it.copy( isLoading = false, - postsResult = listEntity + postsResult = listEntity.copy(posts = filteredPosts) ) } }.onFailure { exception -> @@ -76,11 +77,14 @@ class TapListViewModel @Inject constructor( fun loadFilterOptions() { viewModelScope.launch { - filterOptionRepository.getFilterOptions(userId = 2) + filterOptionRepository.getFilterOptions(userId = userId.first()) .onSuccess { filterEntity -> _state.update { - it.copy(filterOptions = filterEntity) + it.copy( + filterOptions = filterEntity + ) } + Log.e("filterOptions", filterEntity.toString()) } .onFailure { exception -> exception.printStackTrace() @@ -92,11 +96,64 @@ class TapListViewModel @Inject constructor( _state.update { it.copy(selectedSortOption = option) } } + fun updateSortTime(option: String) { + when (option) { + "21분 이내" -> { + _state.update { + it.copy( + selectedSortTimeStart = 0, + selectedSortTimeEnd = 21 + ) + } + } + "21~40분" -> { + _state.update { + it.copy( + selectedSortTimeStart = 21, + selectedSortTimeEnd = 40 + ) + } + } + "41~60분" -> { + _state.update { + it.copy( + selectedSortTimeStart = 41, + selectedSortTimeEnd = 60 + ) + } + } + "1시간 이상" -> { + _state.update { + it.copy( + selectedSortTimeStart = 61, + selectedSortTimeEnd = null + ) + } + } + else -> { + _state.update { + it.copy( + selectedSortTimeStart = null, + selectedSortTimeEnd = null + ) + } + } + } + _state.update { + it.copy( + selectedSortTime = if (it.selectedSortTime == option) "" else option + ) + } + } - fun toggleLike(userId: Int, postId: Int) { + fun toggleLike(postId: Int, isLiked: Boolean) { viewModelScope.launch { - likeRepository.likeCourse(userId = 2, postId = 6) + if (isLiked) { + likeRepository.unlikeCourse(userId = userId.first(), postId = postId) + } else { + likeRepository.likeCourse(userId = userId.first(), postId = postId) + } } } @@ -149,6 +206,10 @@ class TapListViewModel @Inject constructor( } } + fun toggleTimeExpanded() { + _state.update { it.copy(isTimeExpanded = !it.isTimeExpanded) } + } + fun toggleMoodExpanded() { _state.update { it.copy(isMoodExpanded = !it.isMoodExpanded) } } @@ -175,6 +236,7 @@ class TapListViewModel @Inject constructor( selectedSortOption = "", selectedMood = "", selectedDogFriend = "", + selectedSortTime = "", selectedSafety = emptyList(), selectedConvenience = emptyList(), selectedEnvironment = emptyList(), @@ -183,6 +245,7 @@ class TapListViewModel @Inject constructor( isSafetyExpanded = false, isConvenienceExpanded = false, isEnvironmentExpanded = false, + isTimeExpanded = false ) } loadInitialPosts() @@ -198,13 +261,13 @@ class TapListViewModel @Inject constructor( val selectedOptions = buildSelectedOptionsList(currentState) val request = PostsListRequestDto( - durationStart = 0, - durationEnd = 0, + durationStart = state.value.selectedSortTimeStart, + durationEnd = state.value.selectedSortTimeEnd, selectedOptions = selectedOptions.ifEmpty { null } ) postsListRepository.postList( - userId = 2, + userId = userId.first(), request = request ).onSuccess { listEntity -> _state.update { @@ -230,6 +293,8 @@ class TapListViewModel @Inject constructor( val selectedOptions = mutableListOf() val filterOptions = state.filterOptions ?: return emptyList() + + filterOptions.categoryList?.forEach { category -> val selectedOptionIds = mutableListOf() @@ -292,20 +357,4 @@ class TapListViewModel @Inject constructor( fun isFilterApplied(): Boolean { return isAllOptionsSelected() } - -// fun toggleLike(postId: Int, isLiked: Boolean) { -// viewModelScope.launch { -// _state.update { state -> -// val updatedPosts = state.postsResult?.posts?.map { -// if (it.postId == postId) it.copy(isLike = isLiked) else it -// } ?: emptyList() -// -// val updatedPostsResult = state.postsResult?.copy(posts = updatedPosts) -// -// state.copy( -// postsResult = updatedPostsResult -// ) -// } -// } -// } } \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/TapMapScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/TapMapScreen.kt index 4f6d1161..61cd1aab 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/TapMapScreen.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/TapMapScreen.kt @@ -81,7 +81,7 @@ fun TapMapRoute( currentLocation = newLocation ) } - } + } } } } diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/complete/SharedWalkCompletionScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/complete/SharedWalkCompletionScreen.kt index b7a87340..61faa8e3 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/complete/SharedWalkCompletionScreen.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/complete/SharedWalkCompletionScreen.kt @@ -18,10 +18,13 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp 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.component.PawkeyButton import com.paw.key.core.designsystem.component.TopBar @@ -36,7 +39,9 @@ import com.paw.key.presentation.ui.course.walkcomplete.viewmodel.WalkCompleteVie fun SharedWalkCompletionRoute( paddingValues: PaddingValues, navigateUp: () -> Unit, - navigateNext: () -> Unit, + navigateNext: (Int, Int) -> Unit, + routeId: Int, + pageId: Int, modifier: Modifier = Modifier, viewModel: WalkCompleteViewModel = hiltViewModel(), isSharedWalk : Boolean = true @@ -54,7 +59,9 @@ fun SharedWalkCompletionRoute( SharedWalkCompletionScreen( paddingValues = paddingValues, navigateUp = navigateUp, - navigateNext = navigateNext, + navigateNext = { + navigateNext(routeId, pageId) + }, bitmap = state.bitmap, walkRecordList = walkRecordList, totalDistance = state.totalDistance, @@ -110,13 +117,13 @@ fun SharedWalkCompletionScreen( ) ) { // Todo : 사진 받아올 곳 - WalkCompleteHeader( + /*WalkCompleteHeader( bitmap = null, modifier = Modifier .padding(top = 16.dp, start = 16.dp, end = 16.dp) - ) + )*/ - bitmap?.asImageBitmap()?.let { + /*bitmap?.asImageBitmap()?.let { Image( bitmap = it, contentDescription = "My Image", @@ -125,7 +132,17 @@ fun SharedWalkCompletionScreen( .padding(top = 12.dp) .clip(RoundedCornerShape(8.dp)) ) - } + }*/ + AsyncImage( + model = ImageRequest.Builder(LocalContext.current) + .data("https://pawkey-bucket.s3.ap-northeast-2.amazonaws.com/route/69a9c758-csnapshot.jpg") + .crossfade(true) + .build(), + contentDescription = "My Image", + modifier = Modifier + .padding(start = 8.dp, end = 8.dp) + .padding(top = 12.dp) + ) HorizontalDivider( thickness = 1.dp, diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/complete/navigation/SharedWalkCompletionRoute.kt b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/complete/navigation/SharedWalkCompletionRoute.kt index 0d29d01d..9823cd87 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/complete/navigation/SharedWalkCompletionRoute.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/complete/navigation/SharedWalkCompletionRoute.kt @@ -8,6 +8,7 @@ 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.course.sharedwalk.complete.SharedWalkCompletionRoute import com.paw.key.presentation.ui.course.sharedwalk.sharedroute.SharedWalkCourseRoute @@ -15,29 +16,34 @@ import com.paw.key.presentation.ui.course.walk.navigation.WalkCourse import kotlinx.serialization.Serializable fun NavController.navigateSharedWalkCompletion( + pageId: Int, routeId: Int, navOptions: NavOptions?, ) { - navigate(SharedWalkCompletion(routeId), navOptions) + navigate(SharedWalkCompletion(routeId, pageId), navOptions) } @RequiresApi(Build.VERSION_CODES.Q) fun NavGraphBuilder.sharedWalkCompletionNavGraph( paddingValues: PaddingValues, navigateUp: () -> Unit, - navigateNext: () -> Unit, + navigateNext: (Int, Int) -> Unit, snackBarHostState: SnackbarHostState, ) { composable { backStackEntry -> - val routeId = backStackEntry.arguments?.getInt("routeId") ?: 0 + val ids = backStackEntry.toRoute() SharedWalkCompletionRoute( paddingValues = paddingValues, navigateUp = navigateUp, - navigateNext = navigateNext, + navigateNext = { routeId, pageId -> + navigateNext(routeId, pageId) + }, + routeId = ids.routeId, + pageId = ids.pageId ) } } @Serializable -data class SharedWalkCompletion(val routeId: Int) : Route \ No newline at end of file +data class SharedWalkCompletion(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/course/sharedwalk/review/SharedWalkReviewScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/review/SharedWalkReviewScreen.kt index e60aed5a..fb3beae6 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/review/SharedWalkReviewScreen.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/review/SharedWalkReviewScreen.kt @@ -40,6 +40,7 @@ import com.paw.key.core.designsystem.component.PawkeyButton import com.paw.key.core.designsystem.component.SubChip 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.course.sharedwalk.review.state.SharedWalkReviewSideEffect import com.paw.key.presentation.ui.course.sharedwalk.review.viewmodel.SharedWalkReviewViewModel import com.paw.key.presentation.ui.course.walkreview.WalkReviewCategoryUiModel @@ -48,9 +49,7 @@ import com.paw.key.presentation.ui.course.walkreview.component.WalkReviewFeedbac import com.paw.key.presentation.ui.course.walkreview.component.WalkReviewFeedbackHeader import com.paw.key.presentation.ui.course.walkreview.component.WalkReviewImageRow import com.paw.key.presentation.ui.course.walkreview.component.WalkReviewInfoHolder -import com.paw.key.presentation.ui.course.walkreview.component.WalkReviewTextField -import com.paw.key.presentation.ui.course.walkreview.state.WalkReviewContract -import com.paw.key.presentation.ui.course.walkreview.viewmodel.WalkReviewViewModel +import kotlinx.coroutines.flow.first @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @Composable @@ -58,16 +57,26 @@ fun SharedWalkReviewRoute( navigateUp: () -> Unit, navigateNext: () -> Unit, routeId : Int, + pageId : Int, snackBarHostState: SnackbarHostState, modifier: Modifier = Modifier, viewModel: SharedWalkReviewViewModel = hiltViewModel(), isSharedWalk : Boolean = true ) { val state by viewModel.state.collectAsStateWithLifecycle() - val isValid = viewModel.state.collectAsStateWithLifecycle().value.isValidForm + val isValid = state.isValidForm + val userId = PreferenceDataStore.getUserId() val lifecycleOwner = LocalLifecycleOwner.current + LaunchedEffect(Unit) { + viewModel.getSharedWalkReviewCategory(userId.first()) + viewModel.getSharedWalkReviewInfo( + routeId = routeId, + userId = userId.first() + ) + } + LaunchedEffect(viewModel.sideEffect, lifecycleOwner) { viewModel.sideEffect.flowWithLifecycle(lifecycleOwner.lifecycle) .collect { sideEffect -> @@ -137,89 +146,7 @@ fun SharedWalkReviewScreen( modifier = modifier .background(PawKeyTheme.colors.white1) .padding(bottom = 16.dp) - ){ - item { - Text( - text = "제목", - style = PawKeyTheme.typography.head20Sb, - color = PawKeyTheme.colors.green500, - modifier = Modifier - .padding(bottom = 10.dp) - ) - - Row ( - modifier = Modifier - .fillMaxWidth() - .padding(bottom = 10.dp) - ) { - AsyncImage( - model = "", - contentDescription = "profile", - modifier = Modifier - .size(48.dp) - .background( - color = PawKeyTheme.colors.gray50, - shape = CircleShape - ) - .clip(CircleShape) - .padding(end = 10.dp) - ) - - Text( - text = "강아지 이름 작성", - style = PawKeyTheme.typography.body16Sb, - color = PawKeyTheme.colors.gray600 - ) - } - } - - item { - Column( - modifier = Modifier - .padding(bottom = 12.dp, start = 16.dp, end = 16.dp) - .background(PawKeyTheme.colors.white1) - ) { - WalkReviewInfoHolder( - icon = R.drawable.ic_walk_review_location, - content = "강남구 역삼동" - ) - - WalkReviewInfoHolder( - icon = R.drawable.ic_walk_review_time, - content = "2025.06.26(금) | 23:20-23:30" - ) - } - } - - item { - Row ( - modifier = Modifier - .padding(bottom = 12.dp, start = 16.dp, end = 16.dp) - .fillMaxWidth() - .background(PawKeyTheme.colors.white1) - ) { - val chips = listOf("2.2km", "30분", "3208걸음") - - chips.forEach { - SubChip( - text = it, - modifier = Modifier - .padding(end = 6.dp) - ) - } - } - } - - item { - HorizontalDivider( - thickness = 10.dp, - color = PawKeyTheme.colors.gray50, - modifier = Modifier - .fillMaxWidth() - .padding(bottom = 12.dp) - ) - } - + ) { item { WalkReviewFeedbackHeader( petName = petName, @@ -230,11 +157,19 @@ fun SharedWalkReviewScreen( } item { + val emoji = listOf( + "\uD83D\uDE0C", + "\uD83D\uDC36", + "\uD83D\uDEB8", + "\uD83E\uDDFA", + "\uD83C\uDF3F" + ) + feedbackList.forEachIndexed { index, category -> WalkReviewFeedbackForm( icon = R.drawable.ic_walk_review_location, - title = category.categoryDescription, - selectedFeedbackItem = category.options.firstOrNull { it.isSelected }?.optionText, + title = "${emoji[index]}${category.categoryDescription}", + selectedFeedbackItems = category.options.filter { it.isSelected }.map { it.optionText }, feedbackList = category.options.map { it.optionText }, onClickFeedback = { selectedText -> val selectedOption = category.options.find { it.optionText == selectedText } @@ -243,7 +178,8 @@ fun SharedWalkReviewScreen( } }, modifier = Modifier - .padding(top = 12.dp, bottom = 12.dp, start = 16.dp, end = 16.dp) + .padding(top = 12.dp, bottom = 12.dp, start = 16.dp, end = 16.dp), + selectedFeedbackItem = category.options.find { it.isSelected }?.optionText ) } } diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/review/navigation/SharedWalkReviewNavigation.kt b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/review/navigation/SharedWalkReviewNavigation.kt index a75c1b74..58943955 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/review/navigation/SharedWalkReviewNavigation.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/review/navigation/SharedWalkReviewNavigation.kt @@ -9,6 +9,7 @@ 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.course.sharedwalk.complete.SharedWalkCompletionRoute import com.paw.key.presentation.ui.course.sharedwalk.review.SharedWalkReviewRoute @@ -16,10 +17,14 @@ import com.paw.key.presentation.ui.course.walk.navigation.WalkCourse import kotlinx.serialization.Serializable fun NavController.navigateSharedWalkReview( + pageId: Int, routeId: Int, navOptions: NavOptions?, ) { - navigate(SharedWalkReview(routeId), navOptions) + navigate(SharedWalkReview( + routeId = routeId, + pageId = pageId + ), navOptions) } @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @@ -30,16 +35,17 @@ fun NavGraphBuilder.sharedWalkReviewNavGraph( snackBarHostState: SnackbarHostState, ) { composable { backStackEntry -> - val routeId = backStackEntry.arguments?.getInt("routeId") ?: 0 + val ids = backStackEntry.toRoute() SharedWalkReviewRoute( navigateUp = navigateUp, navigateNext = navigateNext, - routeId = routeId, + routeId = ids.routeId, + pageId = ids.pageId, snackBarHostState = snackBarHostState, ) } } @Serializable -data class SharedWalkReview(val routeId : Int) : Route \ No newline at end of file +data class SharedWalkReview(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/course/sharedwalk/review/viewmodel/SharedWalkReviewViewModel.kt b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/review/viewmodel/SharedWalkReviewViewModel.kt index d999818c..72ecd8d7 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/review/viewmodel/SharedWalkReviewViewModel.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/review/viewmodel/SharedWalkReviewViewModel.kt @@ -11,6 +11,7 @@ import com.paw.key.domain.repository.walkreview.WalkReviewRepository import com.paw.key.presentation.ui.course.sharedwalk.review.state.SharedWalkReviewSideEffect import com.paw.key.presentation.ui.course.sharedwalk.review.state.SharedWalkReviewState import com.paw.key.presentation.ui.course.walkreview.util.toUiModel +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -22,6 +23,7 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject +@HiltViewModel class SharedWalkReviewViewModel @Inject constructor( private val repository: WalkReviewRepository, private val sharedRepository : SharedWalkRepository @@ -80,10 +82,10 @@ class SharedWalkReviewViewModel @Inject constructor( } } - fun getSharedWalkReviewInfo(routeId: Int) { + fun getSharedWalkReviewInfo(routeId: Int, userId: Int) { viewModelScope.launch { repository.getWalkReviewInfo( - userId = 2, + userId = userId, routeId = routeId ).onSuccess { _state.update { currentState -> @@ -103,21 +105,25 @@ class SharedWalkReviewViewModel @Inject constructor( } fun onClickFeedback(categoryId: Int, optionId: Int) { - _state.update { current -> - val updatedCategories = current.categoryList.map { category -> - if (category.categoryId == categoryId) { - category.copy( - options = category.options.map { option -> - option.copy(isSelected = option.optionId == optionId) - } - ) - } else category + val updatedCategories = state.value.categoryList.map { category -> + if (category.categoryId == categoryId) { + val updatedOptions = category.options.map { option -> + if (option.optionId == optionId) { + option.copy(isSelected = !option.isSelected) + } else { + option + } + } + category.copy(options = updatedOptions) + } else { + category } - - current.copy(categoryList = updatedCategories) } + + _state.update { it.copy(categoryList = updatedCategories) } } + fun onClickSharedReview() { _state.update { it.copy( diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/sharedroute/SharedWalkCourseScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/sharedroute/SharedWalkCourseScreen.kt index 5c6c74b4..f7df779f 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/sharedroute/SharedWalkCourseScreen.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/sharedroute/SharedWalkCourseScreen.kt @@ -108,7 +108,9 @@ import kotlin.coroutines.resumeWithException fun SharedWalkCourseRoute( paddingValues: PaddingValues, navigateUp: () -> Unit, - navigateNext: () -> Unit, + navigateNext: (Int, Int) -> Unit, + routeId : Int, + pageId : Int, snackBarHostState: SnackbarHostState, modifier: Modifier = Modifier, isSharedWalk : Boolean = true, @@ -120,7 +122,7 @@ fun SharedWalkCourseRoute( val context = LocalContext.current LaunchedEffect(Unit) { - viewModel.getWalkSharedTrack(1) + viewModel.getWalkSharedTrack(routeId) } val totalTime by viewModel.totalTime.collectAsStateWithLifecycle() @@ -223,14 +225,13 @@ fun SharedWalkCourseRoute( sideEffect.message ) - SharedWalkCourseSideEffect.NavigateNext -> navigateNext() + SharedWalkCourseSideEffect.NavigateNext -> navigateNext(routeId, pageId) SharedWalkCourseSideEffect.NavigateUp -> navigateUp() } } } LaunchedEffect(state.isRecording) { - Log.d("WalkCourseRoute", "isRecording: ${state.isRecording}") if (state.isRecording) { try { fusedLocationClient.requestLocationUpdates( @@ -347,7 +348,9 @@ fun SharedWalkCourseRoute( SharedWalkCourseScreen( paddingValues = paddingValues, navigateUp = navigateUp, - navigateNext = navigateNext, + navigateNext = { + navigateNext(routeId, pageId) + }, scope = scope, snackBarHostState = snackBarHostState, mapView = mapView, @@ -419,9 +422,7 @@ fun SharedWalkCourseScreen( modifier = modifier .padding(paddingValues), snackbarHost = { - SnackbarHost( - hostState = snackBarHostState, - ) + } ) { pv -> Box( diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/sharedroute/component/sharedCourseMapView.kt b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/sharedroute/component/sharedCourseMapView.kt index 4f9ab210..0a9ca4a2 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/sharedroute/component/sharedCourseMapView.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/sharedroute/component/sharedCourseMapView.kt @@ -131,6 +131,12 @@ fun sharedWalkCourseMapView( currentDrawnRouteLine = kakaoMap.routeLineManager?.layer?.addRouteLine(routeLineOptions) currentDrawnRouteLine?.show() + + kakaoMapState?.moveCamera( + CameraUpdateFactory.fitMapPoints( + poiPoints.toTypedArray(), 150, 15 + ) + ) } } @@ -233,8 +239,10 @@ fun sharedWalkCourseMapView( } } - LaunchedEffect(Unit) { - trackingManager?.startTracking(centerLabel) + LaunchedEffect(centerLabel, trackingManager) { + if (centerLabel != null && trackingManager != null) { + trackingManager?.startTracking(centerLabel) + } } LaunchedEffect(isPauseTracking) { diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/sharedroute/navigation/SharedWalkNavigation.kt b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/sharedroute/navigation/SharedWalkNavigation.kt index 4a6ff4a0..9127b0ce 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/sharedroute/navigation/SharedWalkNavigation.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/sharedroute/navigation/SharedWalkNavigation.kt @@ -8,33 +8,42 @@ 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.course.sharedwalk.sharedroute.SharedWalkCourseRoute import com.paw.key.presentation.ui.course.walk.navigation.WalkCourse import kotlinx.serialization.Serializable fun NavController.navigateSharedWalkCourse( + routeId: Int, + pageId : Int, navOptions: NavOptions?, ) { - navigate(SharedWalkCourse, navOptions) + navigate(SharedWalkCourse(routeId = routeId, pageId = pageId), navOptions) } @RequiresApi(Build.VERSION_CODES.Q) fun NavGraphBuilder.sharedWalkCourseNavGraph( paddingValues: PaddingValues, navigateUp: () -> Unit, - navigateNext: () -> Unit, + navigateNext: (Int, Int) -> Unit, snackBarHostState: SnackbarHostState, ) { - composable { + composable { backStackEntry -> + val ids = backStackEntry.toRoute() + SharedWalkCourseRoute( paddingValues = paddingValues, navigateUp = navigateUp, - navigateNext = navigateNext, + navigateNext = { routeId, pageId -> + navigateNext(routeId, pageId) + }, + routeId = ids.routeId, + pageId = ids.pageId, snackBarHostState = snackBarHostState, ) } } @Serializable -data object SharedWalkCourse : Route \ No newline at end of file +data class SharedWalkCourse(val pageId: Int, val routeId: Int) : Route \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/sharedroute/viewmodel/SharedWalkViewModel.kt b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/sharedroute/viewmodel/SharedWalkViewModel.kt index ec97f68d..1ec00c70 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/sharedroute/viewmodel/SharedWalkViewModel.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/sharedwalk/sharedroute/viewmodel/SharedWalkViewModel.kt @@ -22,13 +22,13 @@ 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 SharedWalkCourseViewModel @Inject constructor( - @ApplicationContext private val context: Context, private val walkSharedResultRepository : WalkSharedResultRepository, private val sharedWalkRepository: SharedWalkRepository ) : ViewModel() { @@ -40,13 +40,15 @@ class SharedWalkCourseViewModel @Inject constructor( val sideEffect: MutableSharedFlow get() = _sideEffect + private val userId = PreferenceDataStore.getUserId() + private val _totalTime = MutableStateFlow(0L) val totalTime: StateFlow = _totalTime.asStateFlow() fun getWalkSharedTrack(routeId : Int) { viewModelScope.launch { sharedWalkRepository.getSharedWalkTrack( - userId = 2, + userId = userId.first(), routeId = routeId ).onSuccess { _state.update { state -> diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/walk/WalkCourseScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/course/walk/WalkCourseScreen.kt index b1be4e3d..c5b425a2 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/walk/WalkCourseScreen.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/walk/WalkCourseScreen.kt @@ -72,6 +72,7 @@ import com.paw.key.R import com.paw.key.core.designsystem.component.LoadingScreen import com.paw.key.core.designsystem.component.PawkeyButton import com.paw.key.core.designsystem.theme.PawKeyTheme +import com.paw.key.core.util.PreferenceDataStore import com.paw.key.core.util.UiState import com.paw.key.core.util.noRippleClickable import com.paw.key.presentation.ui.course.walk.component.WalkRecordItem @@ -85,6 +86,7 @@ import com.paw.key.presentation.ui.course.walk.viewmodel.WalkCourseViewModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withContext @@ -115,6 +117,8 @@ fun WalkCourseRoute( val lifecycleOwner = LocalLifecycleOwner.current val context = LocalContext.current + val userId = PreferenceDataStore.getUserId() + val totalTime by viewModel.totalTime.collectAsStateWithLifecycle() val formattedTotalTime by remember(totalTime) { @@ -398,7 +402,9 @@ fun WalkCourseRoute( ) } - viewModel.postWalkCourseData(2) + scope.launch { + viewModel.postWalkCourseData(userId = userId.first()) + } }, onCaptured = { bitmap -> viewModel.onMapCaptured(bitmap) diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/walk/component/courseMapView.kt b/app/src/main/java/com/paw/key/presentation/ui/course/walk/component/courseMapView.kt index 7c741436..9c1b0b97 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/walk/component/courseMapView.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/walk/component/courseMapView.kt @@ -256,8 +256,10 @@ fun courseMapView( } } - LaunchedEffect(Unit) { - trackingManager?.startTracking(centerLabel) + LaunchedEffect(centerLabel, trackingManager) { + if (centerLabel != null && trackingManager != null) { + trackingManager?.startTracking(centerLabel) + } } LaunchedEffect(isPauseTracking) { diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/walk/viewmodel/WalkCourseViewModel.kt b/app/src/main/java/com/paw/key/presentation/ui/course/walk/viewmodel/WalkCourseViewModel.kt index 622232e8..f52bd909 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/walk/viewmodel/WalkCourseViewModel.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/walk/viewmodel/WalkCourseViewModel.kt @@ -86,7 +86,7 @@ class WalkCourseViewModel @Inject constructor( CoordinateEntity(it.longitude, it.latitude) }, distance = state.value.totalDistance.toInt(), - duration = (_totalTime.value).toInt(), + duration = (_totalTime.value / 1000).toInt(), startedAt = state.value.startedAt, endedAt = state.value.endedAt, stepCount = state.value.steps.toInt() diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/walkcomplete/WalkCompletionScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/course/walkcomplete/WalkCompletionScreen.kt index e9a641ab..83c4501e 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/walkcomplete/WalkCompletionScreen.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/walkcomplete/WalkCompletionScreen.kt @@ -116,12 +116,12 @@ fun WalkCompletionScreen( shape = RoundedCornerShape(12.dp) ) ) { - // Todo : 사진 받아올 곳 + /*// Todo : 사진 받아올 곳 WalkCompleteHeader( bitmap = null, modifier = Modifier .padding(top = 16.dp, start = 16.dp, end = 16.dp) - ) + )*/ bitmap?.asImageBitmap()?.let { Image( @@ -129,7 +129,7 @@ fun WalkCompletionScreen( contentDescription = "My Image", modifier = Modifier .padding(start = 8.dp, end = 8.dp) - .padding(top = 12.dp) + .padding(top = 32.dp) .clip(RoundedCornerShape(8.dp)) ) } 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 a472f2ac..ac50af71 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 @@ -318,11 +318,19 @@ fun WalkReviewScreen( } item { + val emoji = listOf( + "\uD83D\uDE0C", + "\uD83D\uDC36", + "\uD83D\uDEB8", + "\uD83E\uDDFA", + "\uD83C\uDF3F" + ) + feedbackList.forEachIndexed { index, category -> WalkReviewFeedbackForm( icon = R.drawable.ic_walk_review_location, - title = category.categoryDescription, - selectedFeedbackItem = category.options.firstOrNull { it.isSelected }?.optionText, + title = "${emoji[index]}${category.categoryDescription}", + selectedFeedbackItems = category.options.filter { it.isSelected }.map { it.optionText }, feedbackList = category.options.map { it.optionText }, onClickFeedback = { selectedText -> val selectedOption = category.options.find { it.optionText == selectedText } @@ -331,7 +339,8 @@ fun WalkReviewScreen( } }, modifier = Modifier - .padding(top = 12.dp, bottom = 12.dp, start = 16.dp, end = 16.dp) + .padding(top = 12.dp, bottom = 12.dp, start = 16.dp, end = 16.dp), + selectedFeedbackItem = category.options.find { it.isSelected }?.optionText ) } } @@ -375,7 +384,6 @@ fun WalkReviewScreen( modifier = Modifier .heightIn(min = 200.dp, max = 400.dp) .padding(top = 10.dp, bottom = 24.dp, start = 16.dp, end = 16.dp) - .imePadding() ) } } diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/component/WalkReviewDialog.kt b/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/component/WalkReviewDialog.kt index 3fdcf777..2f9a4f9f 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/component/WalkReviewDialog.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/component/WalkReviewDialog.kt @@ -37,7 +37,7 @@ fun WalkReviewDialog( val progress by animateLottieCompositionAsState( composition, - iterations = Int.MAX_VALUE, + iterations = 1, ) Dialog ( diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/component/WalkReviewFeedbackForm.kt b/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/component/WalkReviewFeedbackForm.kt index 6522d2e3..82714d62 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/component/WalkReviewFeedbackForm.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/component/WalkReviewFeedbackForm.kt @@ -21,6 +21,7 @@ import com.paw.key.presentation.ui.course.walkreview.state.WalkReviewContract fun WalkReviewFeedbackForm( icon: Int, title: String, + selectedFeedbackItems: List, selectedFeedbackItem: String?, // 선택된 옵션 텍스트 feedbackList: List, onClickFeedback: (String) -> Unit, @@ -53,7 +54,7 @@ fun WalkReviewFeedbackForm( verticalArrangement = Arrangement.spacedBy(8.dp) ) { feedbackList.forEach { itemText -> - val isSelected = selectedFeedbackItem == itemText + val isSelected = selectedFeedbackItems.contains(itemText) val textColor = if (isSelected) PawKeyTheme.colors.green500 else PawKeyTheme.colors.gray400 val borderColor = if (isSelected) PawKeyTheme.colors.green500 else PawKeyTheme.colors.gray50 @@ -61,7 +62,7 @@ fun WalkReviewFeedbackForm( item = itemText, textColor = textColor, borderColor = borderColor, - onClickFeedback = onClickFeedback + onClickFeedback = { onClickFeedback(itemText) } ) } } @@ -83,7 +84,8 @@ private fun WalkReviewFeedbackFormPreview() { icon = com.paw.key.R.drawable.ic_walk_review_location, title = "위치", selectedFeedbackItem = null, - onClickFeedback = {} + onClickFeedback = {}, + selectedFeedbackItems = listOf() ) } } \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/component/WalkReviewTextField.kt b/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/component/WalkReviewTextField.kt index 6cea67bb..ddba1349 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/component/WalkReviewTextField.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/component/WalkReviewTextField.kt @@ -28,7 +28,7 @@ fun WalkReviewTextField( onValueChange = { onTextChanged(it) }, - singleLine = true, + singleLine = false, textStyle = PawKeyTheme.typography.body14R, decorationBox = { innerTextField -> Box( diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/navigation/WalkReviewNavigation.kt b/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/navigation/WalkReviewNavigation.kt index b2707e66..e23ca46a 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/navigation/WalkReviewNavigation.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/navigation/WalkReviewNavigation.kt @@ -7,6 +7,7 @@ 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.course.walkreview.WalkReviewRoute import kotlinx.serialization.Serializable @@ -26,18 +27,18 @@ fun NavGraphBuilder.walkReviewNavGraph( snackBarHostState: SnackbarHostState, ) { composable { backStackEntry -> - val routeId = backStackEntry.arguments?.getInt("routeId") ?: 0 + val ids = backStackEntry.toRoute() WalkReviewRoute( navigateUp = navigateUp, navigateNext = { - navigateNext(routeId) + navigateNext(ids.routeId) }, navigateShared = { - navigateShared(routeId) + navigateShared(ids.routeId) }, snackBarHostState = snackBarHostState, - routeId = routeId + routeId = ids.routeId ) } } diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/state/WalkReviewContract.kt b/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/state/WalkReviewContract.kt index 80671ccb..e128d969 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/state/WalkReviewContract.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/state/WalkReviewContract.kt @@ -22,6 +22,7 @@ class WalkReviewContract { val petName: String = "포비", val isPublic: Boolean = false, + val isMine: Boolean = false, val categoryList: List = emptyList() ) { diff --git a/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/viewmodel/WalkReviewViewModel.kt b/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/viewmodel/WalkReviewViewModel.kt index abb35857..8ddd6ace 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/viewmodel/WalkReviewViewModel.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/course/walkreview/viewmodel/WalkReviewViewModel.kt @@ -6,6 +6,7 @@ import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.paw.key.core.util.PhotoUtils +import com.paw.key.core.util.PreferenceDataStore import com.paw.key.data.dto.request.walkreview.WalkCourseReviewRequestDto import com.paw.key.domain.model.entity.walkreview.WalkReviewRecordCategory import com.paw.key.domain.model.entity.walkreview.WalkReviewRecordEntity @@ -20,6 +21,7 @@ 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 @@ -37,9 +39,11 @@ class WalkReviewViewModel @Inject constructor( val sideEffect : MutableSharedFlow get() = _sideEffect + private val userId = PreferenceDataStore.getUserId() fun postWalkReview(routeId : Int, isShare : Boolean) { viewModelScope.launch { + Log.d("WalkReviewViewModel", "${userId.first()}") val category = state.value.categoryList.map { category -> WalkReviewRecordCategory( categoryId = category.categoryId, @@ -52,7 +56,8 @@ class WalkReviewViewModel @Inject constructor( description = state.value.content, isPublic = isShare, categories = category, - routeId = routeId.toLong() + routeId = routeId.toLong(), + isMine = state.value.isMine ) val imageFiles = PhotoUtils.uriListToMultipartParts( @@ -61,7 +66,7 @@ class WalkReviewViewModel @Inject constructor( ) repository.postWalkReview( - userId = 2, + userId = userId.first(), imageFiles = imageFiles, walkReviewRequest = requestEntity ).onSuccess { @@ -78,7 +83,7 @@ class WalkReviewViewModel @Inject constructor( fun getWalkReviewCategory() { viewModelScope.launch { repository.getWalkReviewCategory( - userId = 2 + userId = userId.first() ).onSuccess { _state.update { currentState -> currentState.copy( @@ -96,7 +101,7 @@ class WalkReviewViewModel @Inject constructor( fun getWalkReviewInfo(routeId: Int) { viewModelScope.launch { repository.getWalkReviewInfo( - userId = 2, + userId = userId.first(), routeId = routeId ).onSuccess { _state.update { currentState -> @@ -124,19 +129,22 @@ class WalkReviewViewModel @Inject constructor( } fun onOptionSelected(categoryId: Int, optionId: Int) { - _state.update { current -> - val updatedCategories = current.categoryList.map { category -> - if (category.categoryId == categoryId) { - category.copy( - options = category.options.map { option -> - option.copy(isSelected = option.optionId == optionId) - } - ) - } else category + val updatedList = state.value.categoryList.map { category -> + if (category.categoryId == categoryId) { + val newOptions = category.options.map { option -> + if (option.optionId == optionId) { + option.copy(isSelected = !option.isSelected) + } else { + option + } + } + category.copy(options = newOptions) + } else { + category } - - current.copy(categoryList = updatedCategories) } + + _state.update { it.copy(categoryList = updatedList) } } fun onTitleTextChanged(text : String) { 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 ebab64b4..86484e8f 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 @@ -1,5 +1,7 @@ package com.paw.key.presentation.ui.home +import DistrictDto +import android.util.Log import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -13,6 +15,8 @@ import androidx.compose.foundation.layout.width import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -54,7 +58,6 @@ fun HomeLocationSettingRoute( navigateHomeLocationSetting: () -> Unit, modifier: Modifier = Modifier, ) { - HomeLocationSettingScreen( paddingValues = paddingValues, navigateUp = navigateUp, @@ -76,7 +79,6 @@ fun HomeLocationSettingScreen( val state by viewModel.state.collectAsStateWithLifecycle() val regionList by viewModel.regionList.collectAsStateWithLifecycle() - // 올바른 필드 접근 val selectedGu = state.selectedLocation.selectedGu val selectedDong = state.selectedLocation.selectedDong @@ -164,7 +166,6 @@ fun HomeLocationSettingScreen( enabled = isFormValid, onClick = { if (isFormValid) { - // 올바른 필드 접근 navigateNext(state.selectedLocation.selectedDongId) } } 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 527c1b79..542eb0d3 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 @@ -175,27 +175,21 @@ fun HomeScreen( } } - // posts가 null이 아닐 때만 items를 표시 - posts?.let { postList -> - items( - items = postList, - key = { post -> post.postId } - ) { post -> - CourseCard( - postId = post.postId, - title = post.title, - petName = post.writer.petName, - createdAt = post.createdAt, - representativeImageUrl = post.representativeImageUrl, - petProfileImageUrl = post.writer.petProfileImageUrl, - descriptionTags = post.descriptionTags, - isLiked = post.isLike, - onClickLike = { isLiked -> - onClickLike(post.postId, isLiked) - }, - onClickItem = { navigateNext() } - ) - } + item { + CourseCard( + postId = -1, + title = "제목을 입력해주세요", + petName = "반려견 이름", + createdAt = "2025/07/19", + representativeImageUrl = "https://pawkey-bucket.s3.ap-northeast-2.amazonaws.com/route/69a9c758-csnapshot.jpg", + petProfileImageUrl = "", + descriptionTags = listOf("2.2km"), + isLiked = true, + onClickLike = { isLiked -> + //onClickLike(post.postId, isLiked) + }, + onClickItem = { navigateNext() } + ) } item { Spacer(modifier = Modifier.height(48.dp)) } diff --git a/app/src/main/java/com/paw/key/presentation/ui/home/state/HomeContract.kt b/app/src/main/java/com/paw/key/presentation/ui/home/state/HomeContract.kt index 451c954e..7a3d8953 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/home/state/HomeContract.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/home/state/HomeContract.kt @@ -1,3 +1,4 @@ + package com.paw.key.presentation.ui.home.state import androidx.compose.runtime.Immutable 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 9a736940..f8f3d517 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 @@ -122,7 +122,7 @@ fun LoginScreen( LoginTextField( textValue = email, placeHolder = "사용하실 아이디를 입력해주세요", - isPassword = false, + isPassword = true, onTextChanged = onEmailChanged ) @@ -138,7 +138,7 @@ fun LoginScreen( LoginTextField( textValue = password, placeHolder = "사용하실 비밀번호를 입력해주세요", - isPassword = !isPasswordVisible, + isPassword = isPasswordVisible, onTextChanged = onPasswordChanged, suffix = { Icon( 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 a2d8df9a..ce7a85c2 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 @@ -136,22 +136,37 @@ class MainNavigator( // fun navigateRegional(navOptions: NavOptions? = null) { // navController.navigateRegional(navOptions = navOptions) // } - fun navigateSharedWalkCourse(navOptions: NavOptions? = null) { - navController.navigateSharedWalkCourse(navOptions = navOptions) + fun navigateSharedWalkCourse( + routeId: Int, + pageId : Int, + navOptions: NavOptions? = null) + { + navController.navigateSharedWalkCourse( + routeId = routeId, + pageId = pageId, + navOptions = navOptions + ) } - fun navigateSharedWalkReview(routeId: Int, navOptions: NavOptions? = null) { + fun navigateSharedWalkReview( + pageId: Int, + routeId: Int, + navOptions: NavOptions? = null + ) { navController.navigateSharedWalkReview( + pageId = pageId, routeId = routeId, navOptions = navOptions ) } fun navigateSharedWalkCompletion( + pageId: Int, routeId: Int, navOptions: NavOptions? = null ) { navController.navigateSharedWalkCompletion( + pageId = pageId, routeId = routeId, navOptions = navOptions ) 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 ebc1c562..1f0b55aa 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 @@ -79,11 +79,11 @@ fun PawKeyNavHost( paddingValues = paddingValues, navigateUp = navigator::navigateUp, navigateNext = navigator::navigateWalkCourse, - navigateToDetail = { + navigateToDetail = { postId, routeId -> // Todo : 마찬가지로 이것도 그냥 넣어놓음 나중에 리스트 연결 후 예쩡 / 리스트 아이템이동 navigator.navigateArchivedDetail( - pageId = 20, - routeId = 3 + pageId = postId, + routeId = routeId ) }, setOnVisibleRecord = navigator::setOnVisibleRecord, @@ -93,9 +93,11 @@ fun PawKeyNavHost( sharedWalkCourseNavGraph( paddingValues = paddingValues, navigateUp = navigator::navigateUp, - navigateNext = { - // Todo : 마찬가지로 이것도 그냥 넣어놓음 나중에 리스트 연결 후 예쩡 - navigator::navigateSharedWalkCompletion + navigateNext = { routeId, pageId -> + navigator.navigateSharedWalkCompletion( + routeId = routeId, + pageId = pageId + ) }, snackBarHostState = snackbarHostState ) @@ -103,10 +105,11 @@ fun PawKeyNavHost( sharedWalkCompletionNavGraph( paddingValues = paddingValues, navigateUp = navigator::navigateUp, - navigateNext = { + navigateNext = { routeId, pageId -> // Todo : 마찬가지로 이것도 그냥 넣어놓음 나중에 리스트 연결 후 예쩡 navigator.navigateSharedWalkReview( - routeId = 2 + routeId = routeId, + pageId = pageId ) }, snackBarHostState = snackbarHostState @@ -184,7 +187,7 @@ fun PawKeyNavHost( navigateUp = navigator::navigateUp, navigateNext = { navigator.navigateArchivedDetail( - // Todo : 리스트에서 상세정보 item id 넣어놓기 일단2 + // Todo : 내가 기록 저장한 리스트에서 상세정보 item id 넣어놓기 일단2 pageId = 0, routeId = 2 ) @@ -200,7 +203,13 @@ fun PawKeyNavHost( archivedDetailNavGraph( navigateUp = navigator::navigateUp, - navigateToSharedWalk = navigator::navigateSharedWalkCourse, + navigateToSharedWalk = { routeId, pageId -> + Log.e("navigateNext", "navigateNext : $routeId") + navigator.navigateSharedWalkCourse( + routeId = routeId, + pageId = pageId + ) + }, modifier = modifier ) 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 index f892953b..d39e8a16 100644 --- 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 @@ -3,7 +3,6 @@ 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 @@ -12,16 +11,16 @@ 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.SideEffect 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 @@ -35,24 +34,27 @@ 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: () -> Unit, - routeId: Int, - pageId: Int, + 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.d("LaunchedEffect", "LaunchedEffect: $routeId") - viewModel.getWalkDetail(2, 20) - viewModel.getWalkTopPopular(2, 3) + Log.e("ArchivedDetailRoute", "userId: ${userId.first()}, pageId: $pageId") + viewModel.getWalkDetail(userId.first(), pageId) + viewModel.getWalkTopPopular(userId.first(), routeId) } ArchivedCourseDetailScreen( @@ -76,7 +78,10 @@ fun ArchivedDetailRoute( }, navigateUp = navigateUp, - navigateToSharedWalk = navigateToSharedWalk, + navigateToSharedWalk = { + Log.e("TAG", "ArchivedDetailRoute: $routeId, $pageId", ) + navigateToSharedWalk(routeId, pageId) + }, modifier = modifier ) } @@ -104,19 +109,27 @@ fun ArchivedCourseDetailScreen( var isImageExpanded by remember { mutableStateOf(false) } val isLiked = remember { mutableStateOf(false) } val view = LocalView.current - val window = (view.context as? Activity)?.window val statusBarColor = PawKeyTheme.colors.green500 - SideEffect { - window?.let { - it.statusBarColor = statusBarColor.toArgb() - // it.statusBarColor = PawKeyTheme.colors.green500.toArgb() + 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 = "내가 기록한 산책 루트", @@ -163,14 +176,12 @@ fun ArchivedCourseDetailScreen( ) } } - - if (isImageExpanded) { - ImageModal( - imageUrl = clickImage, - onDismiss = { isImageExpanded = false } - ) - } - + } + if (isImageExpanded) { + ImageModal( + imageUrl = clickImage, + onDismiss = { isImageExpanded = false } + ) } } 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 95c01cea..002c78ae 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 @@ -14,8 +14,10 @@ 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( @@ -25,10 +27,10 @@ fun ArchivedCourseRoute( viewModel: ArchivedListViewModel = hiltViewModel() ) { val state = viewModel.state.collectAsStateWithLifecycle() - + val userId = PreferenceDataStore.getUserId() LaunchedEffect(Unit) { // Todo : userId 네비게이션 연결 - viewModel.getArchivedList(userId = 2) + viewModel.getArchivedList(userId = userId.first()) } ArchivedCourseListScreen( @@ -89,10 +91,8 @@ fun ArchivedCourseListScreenPreview() { state = ArchivedListState(), navigateUp = {}, navigateNext = {}, - onClickLike = { - _, _ -> + 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 index 8652be0a..6094be7c 100644 --- 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 @@ -17,6 +17,7 @@ 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 @@ -28,9 +29,11 @@ import coil.request.ImageRequest import com.paw.key.R import com.paw.key.core.designsystem.component.SubChip import com.paw.key.core.designsystem.theme.PawKeyTheme +import com.paw.key.core.util.PreferenceDataStore 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( @@ -45,10 +48,11 @@ fun MyPageRoute( viewModel: MyPageViewModel = hiltViewModel() ) { val state = viewModel.state.collectAsStateWithLifecycle() + val userId = PreferenceDataStore.getUserId() LaunchedEffect(Unit) { - viewModel.getUserProfiles(userId = 2) - viewModel.getMyPagePetProfiles(userId = 2) + viewModel.getUserProfiles(userId = userId.first()) + viewModel.getMyPagePetProfiles(userId = userId.first()) } MyPageScreen( @@ -219,7 +223,8 @@ fun PetCard( contentDescription = null, modifier = modifier .size(64.dp) - .clip(CircleShape) + .clip(CircleShape), + contentScale = ContentScale.Crop ) Spacer(modifier.width(16.dp)) 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 index 168154c8..31f418eb 100644 --- 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 @@ -11,7 +11,9 @@ 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( @@ -20,9 +22,10 @@ fun UserProfileRoute( viewModel: UserProfileViewModel = hiltViewModel() ) { val state = viewModel.state.collectAsStateWithLifecycle() + val userId = PreferenceDataStore.getUserId() LaunchedEffect(Unit) { - viewModel.getUserProfiles(userId = 2) + viewModel.getUserProfiles(userId = userId.first()) } UserProfileScreen( 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 f39d72ce..a29766cc 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 @@ -5,8 +5,10 @@ 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 com.paw.key.presentation.ui.region.navigation.Regional import kotlinx.serialization.Serializable fun NavController.navigateArchivedDetail( @@ -19,18 +21,19 @@ fun NavController.navigateArchivedDetail( fun NavGraphBuilder.archivedDetailNavGraph( navigateUp: () -> Unit, - navigateToSharedWalk: () -> Unit, + navigateToSharedWalk: (Int, Int) -> Unit, modifier: Modifier = Modifier, ) { composable { backStackEntry -> - val routeId = backStackEntry.arguments?.getInt("routeId") ?: 0 - val pageId = backStackEntry.arguments?.getInt("pageId") ?: 0 + val archivedDetail = backStackEntry.toRoute() ArchivedDetailRoute( navigateUp = navigateUp, - navigateToSharedWalk = navigateToSharedWalk, - routeId = routeId, - pageId = pageId, + navigateToSharedWalk = { routeId, pageId -> + navigateToSharedWalk(routeId, pageId) + }, + routeId = archivedDetail.routeId, + pageId = archivedDetail.pageId, modifier = modifier ) } 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 index 4d4e02f0..4c23009a 100644 --- 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 @@ -3,6 +3,7 @@ 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 @@ -12,6 +13,7 @@ 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 @@ -28,13 +30,15 @@ class SavedListViewModel @Inject constructor( private val _sideEffect = MutableSharedFlow() val sideEffect: MutableSharedFlow = _sideEffect + private val userId = PreferenceDataStore.getUserId() + init { - getSavedList(userId = 2) + getSavedList() } - fun getSavedList(userId: Int) { + fun getSavedList() { viewModelScope.launch { - savedListRepository.getSavedList(userId) + savedListRepository.getSavedList(userId.first()) .onSuccess { result -> _state.update { it.copy( 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 8def8e11..cedb33bd 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 @@ -5,8 +5,10 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable diff --git a/app/src/main/java/com/paw/key/presentation/ui/onboard/component/OnboardPager.kt b/app/src/main/java/com/paw/key/presentation/ui/onboard/component/OnboardPager.kt index aeef3996..6d10c961 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/onboard/component/OnboardPager.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/onboard/component/OnboardPager.kt @@ -1,5 +1,6 @@ package com.paw.key.presentation.ui.onboard.component +import androidx.compose.animation.Crossfade import androidx.compose.foundation.Canvas import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -37,6 +38,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.zIndex import com.paw.key.R +import com.paw.key.core.designsystem.component.PageIndicator import com.paw.key.core.designsystem.theme.PawKeyTheme @Preview(showBackground = true) @@ -59,163 +61,114 @@ private fun PreviewOnboardPager() { @Composable fun OnboardPager( jobList: List, + modifier: Modifier = Modifier, ) { val pageCount = jobList.size val pagerState = rememberPagerState(pageCount = { pageCount }) + val currentPage = pagerState.currentPage + val currentItem = jobList.getOrNull(currentPage) + Box( - modifier = Modifier + modifier = modifier .fillMaxWidth() - .height(620.dp) + .height(LocalConfiguration.current.screenHeightDp.dp * 0.7f) .background( color = PawKeyTheme.colors.white1, - shape = RoundedCornerShape(bottomStart = 16.dp, bottomEnd = 16.dp)) + shape = RoundedCornerShape(bottomStart = 16.dp, bottomEnd = 16.dp) + ) .clip(RoundedCornerShape(bottomStart = 16.dp, bottomEnd = 16.dp)) ) { HorizontalPager( state = pagerState, - modifier = Modifier - .fillMaxSize() + modifier = Modifier.fillMaxSize() ) { page -> - val job = jobList[page] - OnboardingListItem( - title = job.title, - subtitle = job.subtitle, - backImg = job.backImg - ) + OnboardingListItem(backImg = jobList[page].backImg) } - AnimatedPagerIndicator( - pagerState = pagerState, - modifier = Modifier - .align(Alignment.BottomCenter) - .padding(bottom = 20.dp), - activeColor = PawKeyTheme.colors.green500, - inactiveColor = PawKeyTheme.colors.green200 - ) - } -} - -@Composable -fun OnboardingListItem( - title: String, - subtitle: String, - backImg: Int, -) { - Box( - modifier = Modifier - .fillMaxSize() - .background( - color = PawKeyTheme.colors.white1, - shape = RoundedCornerShape(bottomStart = 24.dp, bottomEnd = 24.dp) - ) - .clip( - shape = RoundedCornerShape(bottomStart = 24.dp, bottomEnd = 24.dp) - ) - ) { - - Image( - painter = painterResource(id = backImg), - contentDescription = "sibal", - modifier = Modifier - .fillMaxSize() - .height(596.dp) - .align(Alignment.Center), - contentScale = ContentScale.FillBounds - ) Column( modifier = Modifier + .align(Alignment.TopStart) .padding(top = 80.dp, start = 24.dp, end = 24.dp) - .zIndex(2F) + .zIndex(2f) ) { - val annotatedTitle = if (title.contains("PAWKEY")) { - buildAnnotatedString { - val pawkeyStart = title.indexOf("PAWKEY") - val pawkeyEnd = pawkeyStart + "PAWKEY".length - - withStyle(style = SpanStyle(color = PawKeyTheme.colors.black)) { - append(title.substring(0, pawkeyStart)) + Crossfade(targetState = currentItem?.title) { title -> + title?.let { + val annotated = buildAnnotatedString { + when (currentPage) { + 0 -> { + val start = it.indexOf("PAWKEY") + val end = start + "PAWKEY".length + if (start != -1) { + append(it.substring(0, start)) + withStyle(SpanStyle(color = PawKeyTheme.colors.green500)) { + append("PAWKEY") + } + append(it.substring(end)) + } else { + append(it) + } + } + + 1 -> { + withStyle(SpanStyle(color = PawKeyTheme.colors.green500)) { + append(it) + } + } + + else -> { + withStyle(SpanStyle(color = PawKeyTheme.colors.green500)) { + append(it) + } + } + } } - withStyle(style = SpanStyle(color = PawKeyTheme.colors.green500)) { - append("PAWKEY") - } - - withStyle(style = SpanStyle(color = PawKeyTheme.colors.black)) { - append(title.substring(pawkeyEnd)) - } - } - } else { - buildAnnotatedString { - withStyle(style = SpanStyle(color = PawKeyTheme.colors.green500)) { - append(title) - } + Text( + text = annotated, + style = PawKeyTheme.typography.head24B.copy(lineHeight = 36.sp), + color = PawKeyTheme.colors.black, + ) } } - Text( - text = annotatedTitle, - style = PawKeyTheme.typography.head24B.copy(lineHeight = 36.sp), - color = PawKeyTheme.colors.green500, - modifier = Modifier - ) - if (subtitle.isNotEmpty()) { - Spacer(modifier = Modifier.height(16.dp)) - Text( - text = subtitle, - style = PawKeyTheme.typography.body16M, - color = PawKeyTheme.colors.gray400, - ) + Crossfade(targetState = currentItem?.subtitle) { subtitle -> + subtitle?.takeIf { it.isNotEmpty() }?.let { + Text( + text = it, + style = PawKeyTheme.typography.body16M, + color = PawKeyTheme.colors.gray400, + ) + } } } + PageIndicator( + numberOfPages = pageCount, + selectedPage = currentPage, + selectedColor = PawKeyTheme.colors.green500, + defaultColor = PawKeyTheme.colors.green200, + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(bottom = 20.dp) + ) } } @Composable -fun AnimatedPagerIndicator( - pagerState: PagerState, - modifier: Modifier = Modifier, - activeColor: Color = Color.Blue, - inactiveColor: Color = Color.Gray, - indicatorWidth: Dp = 12.dp, - indicatorHeight: Dp = 12.dp, - spacing: Dp = 12.dp, -) { - val density = LocalDensity.current - val activeIndicatorWidth = 24.dp - - Canvas( - modifier = modifier - .height(indicatorHeight) - .width( - activeIndicatorWidth + (indicatorWidth * (pagerState.pageCount - 1)) + - spacing * (pagerState.pageCount - 1) - ) +fun OnboardingListItem(backImg: Int) { + Box( + modifier = Modifier + .fillMaxSize() + .clip(RoundedCornerShape(bottomStart = 24.dp, bottomEnd = 24.dp)) + .background(PawKeyTheme.colors.white1) ) { - val canvasWidth = size.width - val canvasHeight = size.height - - val indicatorWidthPx = with(density) { indicatorWidth.toPx() } - val activeIndicatorWidthPx = with(density) { activeIndicatorWidth.toPx() } - val indicatorHeightPx = with(density) { indicatorHeight.toPx() } - val spacingPx = with(density) { spacing.toPx() } - - var startX = 0f - - for (i in 0 until pagerState.pageCount) { - val isActive = i == pagerState.currentPage - val currentWidth = if (isActive) activeIndicatorWidthPx else indicatorWidthPx - - drawRoundRect( - color = if (isActive) activeColor else inactiveColor, - topLeft = Offset(startX, (canvasHeight - indicatorHeightPx) / 2), - size = Size(currentWidth, indicatorHeightPx), - cornerRadius = CornerRadius(indicatorHeightPx / 2) - ) - - startX += currentWidth + spacingPx - } + Image( + painter = painterResource(id = backImg), + contentDescription = null, + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.FillBounds + ) } } 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 dd33e98e..4ef74b6e 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 @@ -21,6 +21,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -37,10 +38,13 @@ 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.PreferenceDataStore import com.paw.key.core.util.UiState import com.paw.key.presentation.ui.region.component.regionalMapView import com.paw.key.presentation.ui.region.state.RegionContract import com.paw.key.presentation.ui.region.viewmodel.RegionViewModel +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch @Composable fun RegionalManagementRoute( @@ -56,11 +60,12 @@ fun RegionalManagementRoute( val lifecycleOwner = LocalLifecycleOwner.current val context = LocalContext.current + val userId = PreferenceDataStore.getUserId() + val scope = rememberCoroutineScope() LaunchedEffect(Unit) { - Log.e("regregionId", regionId.toString()) viewModel.getRegionGeometry( - userId = 2, + userId = userId.first(), regionId = regionId, ) } @@ -69,9 +74,12 @@ fun RegionalManagementRoute( viewModel.sideEffect.flowWithLifecycle(lifecycleOwner.lifecycle) .collect { sideEffect -> when (sideEffect) { - is RegionContract.RegionSideEffect.ShowSnackBar -> snackBarHostState.showSnackbar( - sideEffect.message - ) + is RegionContract.RegionSideEffect.ShowSnackBar -> { + snackBarHostState.showSnackbar( + sideEffect.message + ) + navigateNext() + } RegionContract.RegionSideEffect.NavigateNext -> navigateNext() RegionContract.RegionSideEffect.NavigateUp -> navigateUp() @@ -96,8 +104,12 @@ fun RegionalManagementRoute( preRegionName = state.preRegionName, regionName = state.regionName, onClickButton = { - viewModel.patchRegion(userId = 2, regionId = regionId) - navigateNext() + scope.launch { + viewModel.patchRegion( + userId = userId.first(), + regionId = regionId + ) + } }, modifier = modifier ) diff --git a/app/src/main/java/com/paw/key/presentation/ui/signup/viewmodel/SignUpViewModel.kt b/app/src/main/java/com/paw/key/presentation/ui/signup/viewmodel/SignUpViewModel.kt index d2243d46..feba6c15 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/signup/viewmodel/SignUpViewModel.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/signup/viewmodel/SignUpViewModel.kt @@ -19,6 +19,7 @@ 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 okhttp3.MediaType.Companion.toMediaType @@ -46,6 +47,8 @@ class SignUpViewModel @Inject constructor( private var loginEmail: String = "" private var loginPassword: String = "" + private val userId = PreferenceDataStore.getUserId() + init { fetchPetTraits() fetchRegion() @@ -225,7 +228,7 @@ class SignUpViewModel @Inject constructor( private fun fetchPetTraits() { viewModelScope.launch { try { - val result = repository.getOnboardingPets(userId = 2) + val result = repository.getOnboardingPets(userId = userId.first()) result.onSuccess { response -> Log.d( "SignUpViewModel", @@ -272,7 +275,7 @@ class SignUpViewModel @Inject constructor( fun fetchRegion() { viewModelScope.launch { try { - val result = regionRepository.getOnboardingRegion(userId = 2) + val result = regionRepository.getOnboardingRegion(userId = userId.first()) result.onSuccess { response -> Log.d("SignUpViewModel", "Region loaded: ${response.data.districtDtos.size}") _regionList.value = response.data.districtDtos @@ -426,9 +429,10 @@ class SignUpViewModel @Inject constructor( return@launch } + Log.d("SignUpViewModel", "Image processing successful ${userId.first()}") // 서버 요청 val result = infoRepository.postOnboardingInfo( - userId = 2, + userId = userId.first(), image = imagePart, onboardingInfoRequest = request ) diff --git a/app/src/main/res/drawable/ic_community.xml b/app/src/main/res/drawable/ic_community.xml new file mode 100644 index 00000000..5a5be4c8 --- /dev/null +++ b/app/src/main/res/drawable/ic_community.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file