Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
9000dbd
feat: 마이페이지 선호 운동 추가 #282
edv-Shin Nov 24, 2025
38d0378
feat: 더미데이터 추가 #282
edv-Shin Nov 24, 2025
f8e2dc5
refactor: 어댑터 클래스명 변경 #282
edv-Shin Nov 24, 2025
afe8403
feat: api 연결준비 #396
edv-Shin Nov 25, 2025
d104b0e
feat: 선호운동 종류 화면 개발 #396
edv-Shin Nov 25, 2025
4574f4d
fix: 주석 삭제 #282
edv-Shin Nov 27, 2025
543b01d
Merge branch 'feat/preference-exercise-282' into feat/preferred-exerc…
edv-Shin Nov 27, 2025
dde8dde
fix: ktlint 문법 수정 #282
edv-Shin Nov 27, 2025
01c745d
feat: 선호운동 상세설정 레이아웃 구현 #397
edv-Shin Nov 27, 2025
078e3b3
feat: ui 로직 구현 #397
edv-Shin Nov 27, 2025
d22e186
feat: 선호운동 api 연결 준비 #300
edv-Shin Nov 30, 2025
b41a7de
feat: 누락된 요일/숙련도가 있는 경우 예외처리 #300
edv-Shin Dec 1, 2025
fbd0605
Merge branch 'feat/preference-exercise-282' into feat/preferred-exerc…
edv-Shin Dec 1, 2025
e8d4523
refactor: 요일 list를 enum class로 구현 #282
edv-Shin Dec 2, 2025
2532b7b
fix: ktlint 문법 수정 #282
edv-Shin Dec 2, 2025
c45a75b
Merge branch 'feat/preference-exercise-282' into feat/preferred-exerc…
edv-Shin Dec 2, 2025
9e33217
refactor: const값으로 분리 #396
edv-Shin Dec 2, 2025
c252f74
fix: -1 id 인 케이스 고려 #396
edv-Shin Dec 2, 2025
7497441
refactor: 선택된 운동 목록 업데이트 로직 단순화 #396
edv-Shin Dec 2, 2025
441938f
Merge branch 'feat/preferred-exercise-kind-396' into feat/preferred-e…
edv-Shin Dec 2, 2025
d5d3f26
refactor: stringResource 사용하도록 수정 #397
edv-Shin Dec 2, 2025
81a45b6
refactor: stringResource 사용하도록 수정 #397
edv-Shin Dec 2, 2025
4350607
refactor: 선택된 선호 운동 분리 #397
edv-Shin Dec 2, 2025
3b44f26
refactor: 완료버튼 텍스트 로직 수정 #397
edv-Shin Dec 2, 2025
74cffcd
fix: 평일 조건문 수정 #396
edv-Shin Dec 2, 2025
170171b
Merge branch 'feat/preferred-exercise-kind-396' into feat/preferred-e…
edv-Shin Dec 2, 2025
cdbc0d6
fix: 평일 조건문 수정 #282
edv-Shin Dec 2, 2025
f06b583
Merge branch 'feat/preferred-exercise-detail-397' into feat/preferred…
edv-Shin Dec 2, 2025
0d8a3d0
fix: conflict 수정 #300
edv-Shin Dec 2, 2025
14dcf49
refactor: 비교로직을 set으로 변경 #300
edv-Shin Dec 2, 2025
fc68b59
refactor: 기존 데이터 표시 #300
edv-Shin Dec 2, 2025
2f3972a
feat: 매칭 프로필 선호 운동 추가 #406
edv-Shin Dec 3, 2025
57dd776
fix: 프로필 api로 선호운동 조회하도록 변경 #406
edv-Shin Dec 3, 2025
e017bd0
feat: 선호 운동 설정 완료 로딩 구현 #405
edv-Shin Dec 3, 2025
be20d3b
Merge pull request #399 from projects200/feat/preference-exercise-282
edv-Shin Dec 7, 2025
703b20f
Merge branch 'feat/preferred-exercise' into feat/preferred-exercise-k…
edv-Shin Dec 7, 2025
efb1ae0
Merge pull request #400 from projects200/feat/preferred-exercise-kind…
edv-Shin Dec 7, 2025
ed7e71f
Merge branch 'feat/preferred-exercise' into feat/preferred-exercise-d…
edv-Shin Dec 7, 2025
f905fe4
Merge pull request #402 from projects200/feat/preferred-exercise-deta…
edv-Shin Dec 7, 2025
f249428
Merge pull request #404 from projects200/feat/preferred-exercise-api-300
edv-Shin Dec 7, 2025
eda8372
Merge branch 'feat/preferred-exercise' into feat/matching-preferred-e…
edv-Shin Dec 7, 2025
8b004f7
Merge branch 'feat/preferred-exercise-loading-405' into feat/preferre…
edv-Shin Dec 7, 2025
99f4425
Merge branch 'dev' into feat/preferred-exercise
edv-Shin Dec 16, 2025
f5bc3bf
feat: 선호 운동 조회 api 연결
edv-Shin Dec 19, 2025
66cde3a
feat: 선호운동 api 연결
edv-Shin Jan 4, 2026
a601492
Merge remote-tracking branch 'origin/dev' into feat/preferred-exercise
edv-Shin Jan 4, 2026
e6d316a
fix: kitlint 문법 수정
edv-Shin Jan 4, 2026
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 @@ -138,6 +138,7 @@ class MainActivity : AppCompatActivity(), BottomNavigationController {
com.project200.undabang.feature.profile.R.id.urlFormFragment,
com.project200.undabang.feature.profile.R.id.notificationFragment,
com.project200.undabang.feature.profile.R.id.blockMembersFragment,
com.project200.undabang.feature.profile.R.id.preferredExerciseFragment,
com.project200.undabang.feature.matching.R.id.matchingProfileFragment,
com.project200.undabang.feature.matching.R.id.exercisePlaceFragment,
com.project200.undabang.feature.matching.R.id.exercisePlaceSearchFragment,
Expand Down
22 changes: 22 additions & 0 deletions common/src/main/java/com/project200/common/constants/DayOfWeek.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.project200.common.constants

import androidx.annotation.StringRes
import com.project200.undabang.common.R

enum class DayOfWeek(
val index: Int,
@StringRes val resId: Int,
) {
MON(0, R.string.day_mon),
TUE(1, R.string.day_tue),
WED(2, R.string.day_wed),
THU(3, R.string.day_thu),
FRI(4, R.string.day_fri),
SAT(5, R.string.day_sat),
SUN(6, R.string.day_sun),
;

companion object {
fun getByIndex(index: Int): DayOfWeek? = entries.find { it.index == index }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.project200.common.utils

import android.content.Context
import com.project200.common.constants.DayOfWeek
import com.project200.undabang.common.R
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject

class PreferredExerciseDayFormatter
@Inject
constructor(
@ApplicationContext private val context: Context,
) {
/**
* 월요일부터 일요일까지의 선택 여부를 담은 Boolean 리스트를
* 지정된 규칙에 따라 문자열로 포맷합니다.
* @param days 크기가 7인 Boolean 리스트 [월, 화, 수, 목, 금, 토, 일] 순서
* @return 포맷팅된 요일 문자열
*/
fun formatDaysOfWeek(days: List<Boolean>): String {
if (days.size != 7) return ""

// 모든 요일이 포함될 경우 "매일"로 표기
if (days.all { it }) return context.getString(R.string.day_everyday)

val resultParts = mutableListOf<String>()
val weekdays = days.subList(0, 5) // 월요일부터 금요일까지
val weekend = days.subList(5, 7) // 토요일, 일요일

// 평일이 모두 포함될 경우 "평일"로 표기
if (weekdays.all { it }) {
resultParts.add(context.getString(R.string.day_weekdays))
} else {
// 각각 해당하는 평일 날짜를 추가
weekdays.forEachIndexed { index, isSelected ->
if (isSelected) {
addDayName(index, resultParts)
}
}
}

// 주말이 모두 포함될 경우 "주말"로 표기
if (weekend.all { it }) {
resultParts.add(context.getString(R.string.day_weekend))
} else {
// 각각 해당하는 주말 날짜를 추가
weekend.forEachIndexed { index, isSelected ->
if (isSelected) {
addDayName(index + 5, resultParts)
}
}
}

return resultParts.joinToString(", ")
}

private fun addDayName(
index: Int,
resultList: MutableList<String>,
) {
DayOfWeek.getByIndex(index)?.let { day ->
resultList.add(context.getString(day.resId))
}
}
}
16 changes: 16 additions & 0 deletions common/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 요일 -->
<string name="day_mon">월</string>
<string name="day_tue">화</string>
<string name="day_wed">수</string>
<string name="day_thu">목</string>
<string name="day_fri">금</string>
<string name="day_sat">토</string>
<string name="day_sun">일</string>

<!-- 그룹 표기 -->
<string name="day_everyday">매일</string>
<string name="day_weekdays">평일</string>
<string name="day_weekend">주말</string>
</resources>
37 changes: 37 additions & 0 deletions data/src/main/java/com/project200/data/api/ApiService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.project200.data.api

import com.project200.data.dto.BaseResponse
import com.project200.data.dto.CustomTimerIdDTO
import com.project200.data.dto.DeletePreferredExerciseDTO
import com.project200.data.dto.EditExercisePlaceDTO
import com.project200.data.dto.ExerciseIdDto
import com.project200.data.dto.ExpectedScoreInfoDTO
Expand All @@ -20,6 +21,8 @@ import com.project200.data.dto.GetMatchingMembersDto
import com.project200.data.dto.GetMatchingProfileDTO
import com.project200.data.dto.GetNewChattingMessagesDTO
import com.project200.data.dto.GetOpenChatUrlDTO
import com.project200.data.dto.GetPreferredExerciseDTO
import com.project200.data.dto.GetPreferredExerciseTypeDTO
import com.project200.data.dto.GetProfileDTO
import com.project200.data.dto.GetProfileImageResponseDto
import com.project200.data.dto.GetScoreDTO
Expand All @@ -37,6 +40,7 @@ import com.project200.data.dto.PostExerciseRequestDto
import com.project200.data.dto.PostExerciseResponseDTO
import com.project200.data.dto.PostLoginRequest
import com.project200.data.dto.PostMessageResponse
import com.project200.data.dto.PostPreferredExerciseDTO
import com.project200.data.dto.PostSignUpData
import com.project200.data.dto.PostSignUpRequest
import com.project200.data.dto.PutProfileRequest
Expand All @@ -49,6 +53,7 @@ import okhttp3.MultipartBody
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.HTTP
import retrofit2.http.Header
import retrofit2.http.Multipart
import retrofit2.http.PATCH
Expand Down Expand Up @@ -170,6 +175,38 @@ interface ApiService {
@AccessTokenApi
suspend fun getBlockedMembers(): BaseResponse<List<GetBlockedMemberDTO>>

/** 선호 운동 */
// 선호 운동 조회
@GET("api/v1/preferred-exercises")
@AccessTokenApi
suspend fun getPreferredExercises(): BaseResponse<List<GetPreferredExerciseDTO>>

// 선호 운동 종류 조회
@GET("api/v1/exercise-types")
@AccessTokenApi
suspend fun getPreferredExerciseTypes(): BaseResponse<List<GetPreferredExerciseTypeDTO>>

// 선호 운동 생성
@POST("api/v1/preferred-exercises")
@AccessTokenApi
suspend fun postPreferredExercises(
@Body preferredExercises: List<PostPreferredExerciseDTO>,
): BaseResponse<Unit?>

// 선호 운동 삭제
@HTTP(method = "DELETE", path = "api/v1/preferred-exercises", hasBody = true)
@AccessTokenApi
suspend fun deletePreferredExercises(
@Body preferredExerciseIds: DeletePreferredExerciseDTO,
): BaseResponse<Unit?>

// 선호 운동 수정
@PATCH("api/v1/preferred-exercises")
@AccessTokenApi
suspend fun patchPreferredExercises(
@Body preferredExercises: List<PostPreferredExerciseDTO>,
): BaseResponse<Unit?>

/** 운동 기록 */
// 구간별 운동 기록 횟수 조회
@GET("api/v1/exercises/count")
Expand Down
2 changes: 1 addition & 1 deletion data/src/main/java/com/project200/data/dto/MatchingDTO.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ data class GetMatchingProfileDTO(
val yearlyExerciseDays: Int,
val exerciseCountInLast30Days: Int,
val exerciseScore: Int,
val preferredExercises: List<PreferredExerciseDTO>,
val preferredExercises: List<GetProfilePreferredExerciseDTO>,
)

@JsonClass(generateAdapter = true)
Expand Down
37 changes: 33 additions & 4 deletions data/src/main/java/com/project200/data/dto/MemberDTO.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,45 @@ data class GetProfileDTO(
val yearlyExerciseDays: Int,
val exerciseCountInLast30Days: Int,
val exerciseScore: Int,
val preferredExercises: List<PreferredExerciseDTO>,
val preferredExercises: List<GetProfilePreferredExerciseDTO>,
)

@JsonClass(generateAdapter = true)
data class PreferredExerciseDTO(
val preferredExerciseId: Int,
data class GetProfilePreferredExerciseDTO(
val preferredExerciseId: Long,
val name: String,
val skillLevel: String,
val daysOfWeek: List<Boolean>,
val imageUrl: String,
val imageUrl: String?,
)

@JsonClass(generateAdapter = true)
data class GetPreferredExerciseDTO(
val preferredExerciseId: Long,
val exerciseTypeId: Long,
val exerciseName: String,
val skillLevel: String,
val daysOfWeek: List<Boolean>,
val imageUrl: String?,
)

@JsonClass(generateAdapter = true)
data class GetPreferredExerciseTypeDTO(
val exerciseId: Long,
val exerciseName: String,
val imageUrl: String?,
)

@JsonClass(generateAdapter = true)
data class PostPreferredExerciseDTO(
val exerciseTypeId: Long,
val skillLevel: String,
val daysOfWeek: List<Boolean>,
)

@JsonClass(generateAdapter = true)
data class DeletePreferredExerciseDTO(
val preferredExerciseIds: List<Long>,
)

@JsonClass(generateAdapter = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,24 @@ import android.content.Context
import androidx.core.net.toUri
import com.project200.common.di.IoDispatcher
import com.project200.data.api.ApiService
import com.project200.data.dto.DeletePreferredExerciseDTO
import com.project200.data.dto.GetBlockedMemberDTO
import com.project200.data.dto.GetOpenChatUrlDTO
import com.project200.data.dto.GetPreferredExerciseDTO
import com.project200.data.dto.GetPreferredExerciseTypeDTO
import com.project200.data.dto.GetProfileDTO
import com.project200.data.dto.GetProfileImageResponseDto
import com.project200.data.dto.GetScoreDTO
import com.project200.data.dto.PutProfileRequest
import com.project200.data.mapper.toModel
import com.project200.data.mapper.toMultipartBodyPart
import com.project200.data.mapper.toPostDto
import com.project200.data.utils.apiCallBuilder
import com.project200.domain.model.BaseResult
import com.project200.domain.model.BlockedMember
import com.project200.domain.model.ExerciseType
import com.project200.domain.model.OpenUrl
import com.project200.domain.model.PreferredExercise
import com.project200.domain.model.ProfileImageList
import com.project200.domain.model.Score
import com.project200.domain.model.UserProfile
Expand Down Expand Up @@ -158,6 +164,50 @@ class MemberRepositoryImpl
)
}

override suspend fun getPreferredExercises(): BaseResult<List<PreferredExercise>> {
return apiCallBuilder(
ioDispatcher = ioDispatcher,
apiCall = { apiService.getPreferredExercises() },
mapper = { dtoList: List<GetPreferredExerciseDTO>? ->
dtoList?.map { it.toModel() } ?: throw NoSuchElementException()
},
)
}

override suspend fun getPreferredExerciseTypes(): BaseResult<List<ExerciseType>> {
return apiCallBuilder(
ioDispatcher = ioDispatcher,
apiCall = { apiService.getPreferredExerciseTypes() },
mapper = { dtoList: List<GetPreferredExerciseTypeDTO>? ->
dtoList?.map { it.toModel() } ?: throw NoSuchElementException()
},
)
}

override suspend fun createPreferredExercise(preferredExercises: List<PreferredExercise>): BaseResult<Unit> {
return apiCallBuilder(
ioDispatcher = ioDispatcher,
apiCall = { apiService.postPreferredExercises(preferredExercises.map { it.toPostDto() }) },
mapper = { Unit },
)
}

override suspend fun editPreferredExercise(preferredExercises: List<PreferredExercise>): BaseResult<Unit> {
return apiCallBuilder(
ioDispatcher = ioDispatcher,
apiCall = { apiService.patchPreferredExercises(preferredExercises.map { it.toPostDto() }) },
mapper = { Unit },
)
}

override suspend fun deletePreferredExercise(preferredExerciseIds: List<Long>): BaseResult<Unit> {
return apiCallBuilder(
ioDispatcher = ioDispatcher,
apiCall = { apiService.deletePreferredExercises(DeletePreferredExerciseDTO(preferredExerciseIds)) },
mapper = { Unit },
)
}

companion object {
const val IMAGE_PART_ERROR = "IMAGE_PART_ERROR"
}
Expand Down
11 changes: 1 addition & 10 deletions data/src/main/java/com/project200/data/mapper/MatchingMapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import com.project200.domain.model.ExercisePlace
import com.project200.domain.model.Location
import com.project200.domain.model.MatchingMember
import com.project200.domain.model.MatchingMemberProfile
import com.project200.domain.model.PreferredExercise

fun GetMatchingMembersDto.toModel(): MatchingMember {
return MatchingMember(
Expand Down Expand Up @@ -43,15 +42,7 @@ fun GetMatchingProfileDTO.toModel(): MatchingMemberProfile {
exerciseCountInLast30Days = this.exerciseCountInLast30Days,
exerciseScore = this.exerciseScore,
preferredExercises =
this.preferredExercises.map {
PreferredExercise(
preferredExerciseId = it.preferredExerciseId,
name = it.name,
skillLevel = it.skillLevel,
daysOfWeek = it.daysOfWeek,
imageUrl = it.imageUrl,
)
},
this.preferredExercises.map { it.toModel() },
)
}

Expand Down
Loading