Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a551e86
chore/#105: single 라인, 카테고리 여러개 선택 가능
sonms Jul 16, 2025
597946f
chore/#105: 이모지 붙이기
sonms Jul 16, 2025
fdd1b2b
chore/#105: 상세보기 연결
sonms Jul 17, 2025
561b963
feat/#105: 커뮤니티 구현
sonms Jul 17, 2025
a50ded4
chore/#105: 코스 맵, 네비게이션 디테일 수정
sonms Jul 17, 2025
cd961e0
chore/#105: 리플 및 데이터 연동 수정
sonms Jul 17, 2025
5d16038
chore/#105: toggle 옵션추가
sonms Jul 17, 2025
51925e3
chore/#105: 필터 불러오기 api 수정
sonms Jul 17, 2025
33a0c20
fix/#105: 홈 로케이션 오류 수정
sonms Jul 17, 2025
c37eeb5
chore/#105: 분기 오류 수정
sonms Jul 17, 2025
7ccf6e4
chore/#105: 리뷰하기 후 상세보기로 이동
sonms Jul 17, 2025
c8fffc1
chore/#105: 애니메이션 1로, 서버에서 추가된 부분 반영
sonms Jul 17, 2025
d023006
chore/#105: 복수 선택, 네비게이션, state
sonms Jul 17, 2025
7c22e91
chore/#105: 데이터 전송, ui 수정
sonms Jul 17, 2025
45af694
chore/#105: 탭 아이템, 상태관리 수정
sonms Jul 17, 2025
17bcbe4
feat/#105: userid 가져오기
sonms Jul 17, 2025
9fc9d58
chore/#105: 공유 하기 뷰 로직,데이터,연결
sonms Jul 17, 2025
a39863b
chore/#105: 분기 처리 , userid 처리
sonms Jul 17, 2025
9c9f1a3
chore/#105: 이것도 userid, routeid 분기처리
sonms Jul 17, 2025
ef7c888
chore/#105: 온보딩 추천
sonms Jul 17, 2025
808c609
chore/#105: noripple추가
sonms Jul 17, 2025
a8332ae
chore/#105: 네비게이션 파라미터 수정
sonms Jul 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -41,6 +42,8 @@ fun ImageModal(
model = imageUrl,
contentDescription = null,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
)

// AsyncImage(
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
)
}
}
Original file line number Diff line number Diff line change
@@ -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<Interaction>()

override suspend fun emit(interaction: Interaction) { }

override fun tryEmit(interaction: Interaction): Boolean = true
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ data class WalkCourseReviewRequestDto(
@SerialName("isPublic")
val isPublic: Boolean,

@SerialName("selectedCategories")
@SerialName("isMine")
val isMine: Boolean,

@SerialName("selectedOptionsForCategories")
val selectedCategories: List<SelectedCategoryDto>,

@SerialName("routeId")
Expand Down
Original file line number Diff line number Diff line change
@@ -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<SelectDto>,
@SerialName("categoryList")
val categoryList: List<CategoryDto>
)
) {
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<OptionDto>? = 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(
Expand All @@ -27,14 +65,30 @@ data class CategoryDto(
val categoryDescription: String? = null,
@SerialName("categoryName")
val categoryName: String? = null,
@SerialName("categoryOptions")
@SerialName("options")
val categoryOptions: List<CategoryOptionDto>? = 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
)
) {
fun toEntity(): CategoryOption {
return CategoryOption(
categoryOptionId = categoryOptionId ?: 0,
categoryOptionText = categoryOptionText ?: ""
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -56,7 +60,9 @@ fun PostDto.toEntity(): PostEntity {
title = title,
representativeImageUrl = representativeImageUrl,
routeId = routeId,
isMine = isMine,
writer = writer.toEntity(),
isPublic = isPublic,
descriptionTags = descriptionTags
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,5 @@ import javax.inject.Inject
class FilterOptionDataSource @Inject constructor(
private val filterOptionService: FilterOptionService
) {
suspend fun getFilterOptions(userId: Int): BaseResponse<FilterOptionResponse> {
return filterOptionService.getFilterOptions(userId)
}
suspend fun getFilterOptions(userId: Int) = filterOptionService.getFilterOptions(userId).data
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -17,7 +18,7 @@ class WalkReviewDataSource @Inject constructor(
userId: Int,
imageFiles: List<MultipartBody.Part>,
walkReviewRequestDto: WalkCourseReviewRequestDto
) : BaseResponse<Unit> {
) : BaseResponse<WalkReviewResponseDto> {
val jsonString = Json.encodeToString(WalkCourseReviewRequestDto.serializer(), walkReviewRequestDto)
val requestBody = jsonString.toRequestBody("application/json".toMediaType())

Expand Down
Loading