Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
0da5765
[feat] #78 URL 상수 정의
kamja0510 Jan 16, 2026
8b6ebe7
[feat] #78 이용 약관 enum 타입에 Url 변수 추가
kamja0510 Jan 16, 2026
c66a82c
[feat] #78 약관 Row의 arrow 아이콘 클릭 시 노션 페이지 띄우도록 구현
kamja0510 Jan 16, 2026
24d5611
[feat] #78 프로필 사진 기본 이미지 수정
kamja0510 Jan 16, 2026
d446081
[feat] #78 온보딩 pager 1,2 페이지에 사진 추가
kamja0510 Jan 16, 2026
7e49627
[refactor] #78 온보딩 프로필 사진 설정 화면에서 profileImagePath를 Uri객체에서 String 객체…
kamja0510 Jan 16, 2026
7384ba4
[refactor] #78 프로필 사진 영역 컴포넌트화
kamja0510 Jan 16, 2026
129b87a
[feat] #78 setting screen 웹뷰 띄우기 로직 구현
kamja0510 Jan 17, 2026
cd72590
[fix] #78 프로필 수정 api 에서 닉네임 수정 안되는 오류 수정
kamja0510 Jan 17, 2026
ff5c2fd
[feat] #78 등록하기 클릭 후 홈으로 이동 구현
kamja0510 Jan 17, 2026
c732e4f
[feat] #78 커뮤니티 가이드 버튼 클릭 시 커뮤니티 가이드 노션 페이지로 이동 구현
kamja0510 Jan 17, 2026
a2afd84
[feat] #78 보관한 곡 클릭 시 곡 상세 페이지로 이동 구현
kamja0510 Jan 17, 2026
b1a21f9
[feat] #78 MyPage Screen의 함수들 드릴링 하고 bottomNavigationController 만들어 전…
kamja0510 Jan 18, 2026
0bb63e0
[feat] #78 케밥 아이콘 클릭 시 바텀시트 올라오도록 구현
kamja0510 Jan 18, 2026
9e1be81
Merge branch 'develop' into feat/#78-qa-1-ui
kamja0510 Jan 18, 2026
87f36f1
[fix] #78 bottomsheet 디자인 수정
kamja0510 Jan 18, 2026
6ec71d0
[feat] #78 등록한 곡 삭제 구현
kamja0510 Jan 18, 2026
a3f2b5f
[feat] #78 navDisplay의 애니메이션 다 없에고 bottombar에 애니메이션 추가
kamja0510 Jan 18, 2026
7fa46e4
[feat] #78 bottomBar에 애니메이션 되돌리기
kamja0510 Jan 18, 2026
8a12512
[feat] #78 등록된 곡 클릭 시 상세 곡 페이지로 이동
kamja0510 Jan 18, 2026
bdf8c38
[feat] #78 알림 권한 붙이기 아직 안됨
kamja0510 Jan 18, 2026
57e98f6
[chore] #78 ktlint
kamja0510 Jan 18, 2026
8433350
[feat] #78 splash 시간 상수 1000L으로 변경
kamja0510 Jan 18, 2026
24f5c6d
Merge branch 'develop' into feat/#78-qa-1-ui
kamja0510 Jan 18, 2026
5c71b12
[feat] #78 baseimage 처리 구현
kamja0510 Jan 18, 2026
6c8df6c
[feat] #78 탭 로우 아래에 회색 박스 두기
kamja0510 Jan 18, 2026
3a6789d
[chore] #78 ktlint
kamja0510 Jan 20, 2026
36d4953
[fix] #83 로그인 화면 로고와 텍스트 사이 간격 수정
kamja0510 Jan 20, 2026
dce079e
[fix] #83 프로필 설정 화면 topbar와 텍스트 사이 간격 수정
kamja0510 Jan 20, 2026
e9e39bd
[feat] #83 닉네임 중복 검증 시 오류 문구 뜨도록 구현
kamja0510 Jan 20, 2026
491f29c
[fix] #83 프로필 설정 화면 버튼 label 가입하기로 변경
kamja0510 Jan 21, 2026
66aa80a
[fix] #83 프로필 이미지가 작을 경우 전체 사이즈를 채우지 않는 현상 수정
kamja0510 Jan 21, 2026
3db3309
[feat] #83 온보딩 화면에서 뒤로가기 버튼 없애고 제스처로 뒤로가기 할 시 로그인 화면으로 이동 구현
kamja0510 Jan 21, 2026
d6d2660
[chore] #83 ktlint
kamja0510 Jan 21, 2026
0ff5fce
trigger ci manual
kamja0510 Jan 21, 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
21 changes: 21 additions & 0 deletions core/common/src/main/java/com/example/common/constant/Url.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.example.common.constant

object Url {
// 서비스 이용 약관
const val TERMS_OF_SERVICE = "https://www.notion.so/2d13aeb558c9801fb8c2db2ae6ac2c3e?source=copy_link"

// 개인정보 처리 방침
const val PRIVACY_POLICY = "https://www.notion.so/2d13aeb558c98003b480f83b06245430?source=copy_link"

// 공지사항
const val ANNOUNCEMENT = "https://www.notion.so/2d13aeb558c980919796c2b4d7109369?source=copy_link"

// 커뮤니티 가이드
const val COMMUNITY_GUIDE = "https://www.notion.so/2d13aeb558c980c7915bf540db799aac?source=copy_link"

// 문의/제안하기
const val INQUIRY = "https://forms.gle/reQb2nmhjSqXVvnq7"

// 에러 뷰
const val ERROR = ""
}
7 changes: 5 additions & 2 deletions core/common/src/main/java/com/example/common/type/TermType.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.example.common.type

import com.example.common.constant.Url

enum class TermType(
val isMandatory: Boolean,
val url: String,
) {
TERMS_OF_SERVICE(isMandatory = true), // 서비스 이용약관 (필수)
PRIVACY_POLICY(isMandatory = true), // 개인정보 처리방침 (필수)
TERMS_OF_SERVICE(isMandatory = true, url = Url.TERMS_OF_SERVICE), // 서비스 이용약관 (필수)
PRIVACY_POLICY(isMandatory = true, url = Url.PRIVACY_POLICY), // 개인정보 처리방침 (필수)
;

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ package com.example.data.constant
object ErrorCode {
const val USER_NOT_FOUND = 4041
const val EXPIRED_ACCESS_TOKEN = 4012
const val DUPLICATED_NICKNAME = 4090
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import retrofit2.HttpException
import timber.log.Timber
import java.io.File
import java.net.HttpURLConnection
import javax.inject.Inject
Expand Down Expand Up @@ -84,7 +85,25 @@ class AuthRemoteDataSource
)

return response.data ?: throw Exception("Data is null")
} catch (e: Exception) {
} catch (e: HttpException) {
if (e.code() == HttpURLConnection.HTTP_CONFLICT) {
val errorString = e.response()?.errorBody()?.string()
Timber.d("errorString : $errorString")

if (errorString != null) {
try {
val errorResponse = json.decodeFromString<BaseResponse<String?>>(errorString)
Timber.d("errorResponse : $errorResponse")
if (errorResponse.code == ErrorCode.DUPLICATED_NICKNAME) {
throw NetworkException(ErrorCode.DUPLICATED_NICKNAME, errorResponse.message)
}
} catch (e: SerializationException) {
// JSON 형식이 잘못됨 (괄호 누락 등)
} catch (e: IllegalArgumentException) {
// 데이터 타입 불일치
}
}
}
throw e
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.example.data.datasource.remote

import com.example.data.model.request.NotificationRequest
import com.example.data.model.request.PatchProfileRequest
import com.example.data.model.response.UserInfoResponse
import com.example.data.service.UserService
import kotlinx.serialization.InternalSerializationApi
Expand Down Expand Up @@ -32,7 +33,10 @@ class UserRemoteDataSource

userService.patchProfile(
profileImg = imagePart,
request = nickname,
request =
PatchProfileRequest(
nickname,
),
)
} catch (e: Exception) {
Timber.e(e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ fun UserResponse.toDomain(): Writer =
Writer(
userId = this.userId,
nickname = this.nickname,
profileImg = this.profileImg ?: "",
profileImg = this.profileImg,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example.data.model.request

import kotlinx.serialization.Serializable

@Serializable
data class PatchProfileRequest(
val nickname: String?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.example.data.datasource.remote.KakaoLoginDataSource
import com.example.data.datasource.remote.UserRemoteDataSource
import com.example.data.model.request.LoginRequest
import com.example.data.model.request.SignupRequest
import com.example.domain.model.NicknameValidationResult
import com.example.domain.model.User
import com.example.domain.repository.AuthRepository
import com.example.network.NetworkException
Expand Down Expand Up @@ -64,21 +65,27 @@ class AuthRepositoryImpl
kakaoAccessToken: String?,
profileImage: String?,
nickname: String,
): Result<Unit> =
): Result<NicknameValidationResult> =
runCatching {
val profileFile = fileLocalDataSource.createAndGetFile(profileImage)

val response =
authRemoteDataSource.signup(
kakaoAccessToken = kakaoAccessToken,
imageFile = profileFile,
signupRequest =
SignupRequest(
platform = KAKAO_PLATFORM,
nickname = nickname,
),
)

try {
authRemoteDataSource.signup(
kakaoAccessToken = kakaoAccessToken,
imageFile = profileFile,
signupRequest =
SignupRequest(
platform = KAKAO_PLATFORM,
nickname = nickname,
),
)
} catch (e: NetworkException) {
if (e.code == ErrorCode.DUPLICATED_NICKNAME) {
return Result.success(NicknameValidationResult.Error.Duplicated)
}
throw e
}
tokenLocalDataSource.saveTokens(
accessToken = response.accessToken,
refreshToken = response.refreshToken,
Expand All @@ -91,6 +98,8 @@ class AuthRepositoryImpl
profileImagePath = profileFile?.absolutePath,
),
)

return Result.success(NicknameValidationResult.Success)
}

override suspend fun withdraw(): Result<Unit> =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.example.data.constant.ApiConstants.SCRAPS
import com.example.data.constant.ApiConstants.USERS
import com.example.data.constant.ApiConstants.VERSIONS
import com.example.data.model.request.NotificationRequest
import com.example.data.model.request.PatchProfileRequest
import com.example.data.model.response.BaseResponse
import com.example.data.model.response.NotificationResponse
import com.example.data.model.response.RegisteredTracksResponse
Expand All @@ -30,7 +31,7 @@ interface UserService {
@PATCH("$API/$VERSIONS/$USERS/$ME")
suspend fun patchProfile(
@Part profileImg: MultipartBody.Part?,
@Part("nickname") request: String?,
@Part("changeProfileRequest") request: PatchProfileRequest,
): BaseResponse<Unit>

@GET("$API/$VERSIONS/$USERS/{userId}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ fun DPlayLargeCover(
isBookmarkChecked: Boolean,
isLikeChecked: Boolean,
likeCount: Int,
writerProfileImageUrl: String,
writerProfileImageUrl: String?,
writerNickname: String,
content: String,
musicImageUrl: String,
Expand Down Expand Up @@ -130,7 +130,7 @@ fun DPlayLargeCover(
modifier = Modifier.noRippleClickable(onClick = onWriterProfileClick),
) {
AsyncImage(
model = writerProfileImageUrl,
model = writerProfileImageUrl ?: R.drawable.base_profile_image,
contentDescription = null,
modifier =
Modifier
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.example.designsystem.component

import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import com.dplay.designsystem.R
import com.example.designsystem.theme.DPlayTheme
import com.example.designsystem.util.noRippleClickable

@Composable
fun DPlayProfileImageArea(
onProfileImageClick: () -> Unit,
profileImagePath: String?,
modifier: Modifier = Modifier,
content: @Composable () -> Unit = {},
) {
Box(
modifier =
modifier
.noRippleClickable(
onClick = { onProfileImageClick() },
),
contentAlignment = Alignment.BottomEnd,
) {
AsyncImage(
model = profileImagePath ?: R.drawable.base_profile_image,
contentDescription = null,
modifier =
Modifier
.fillMaxSize()
.clip(CircleShape)
.border(
width = 1.dp,
color = DPlayTheme.colors.gray200,
shape = CircleShape,
),
contentScale = ContentScale.Crop,
)

content()
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion core/designsystem/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

<string name="nickname_error_not_enough_length">%d자 이상 입력해주세요</string>
<string name="nickname_error_invalid_format">특수문자, 띄어쓰기는 사용 불가능해요</string>
<string name="nickname_error_already_exists">이미 사용 중인 값입니다</string>
<string name="nickname_error_already_exists">이미 사용 중인 닉네임이에요</string>
<string name="nickname_error_forbidden_word">비속어, 금칙어가 포함되어 있습니다</string>
<string name="nickname_success_message">사용 가능한 닉네임이에요</string>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ sealed interface NicknameValidationResult {
sealed interface Error : NicknameValidationResult {
data object TooShort : Error
data object InvalidFormat : Error
data object Duplicated : Error
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package com.example.domain.model
data class Writer(
val userId: Long,
val nickname: String,
val profileImg: String,
val profileImg: String?,
)
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package com.example.domain.repository

import com.example.domain.model.NicknameValidationResult

interface AuthRepository {
suspend fun kakaoLogin(): Result<String>

suspend fun signupWithKakao(
kakaoAccessToken: String?,
profileImage: String?,
nickname: String,
): Result<Unit>
): Result<NicknameValidationResult>

suspend fun logout(): Result<Unit>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.example.ui.controller

import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue

class BottomNavigationController {
var bottomNavigationVisible by mutableStateOf(true)
private set

fun show() {
bottomNavigationVisible = true
}

fun hide() {
bottomNavigationVisible = false
}
}

val LocalBottomNavigationController =
compositionLocalOf<BottomNavigationController> {
error("BottomNavigationController not provided")
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ fun NicknameValidationResult.toUiState(): InputState =
NicknameValidationResult.Success -> NicknameInputState.Success
NicknameValidationResult.Error.TooShort -> NicknameInputState.Error.NotEnoughLength
NicknameValidationResult.Error.InvalidFormat -> NicknameInputState.Error.InvalidFormat
NicknameValidationResult.Error.Duplicated -> NicknameInputState.Error.AlreadyExists
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,9 @@ class CommentContract {
data object NavigateToBack : CommentSideEffect

data object NavigateToHome : CommentSideEffect

data class OpenWebView(
val url: String,
) : CommentSideEffect
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.layout
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
Expand Down Expand Up @@ -47,6 +48,8 @@ fun CommentRoute(
) {
val state by viewModel.uiState.collectAsStateWithLifecycle()

val uriHandler = LocalUriHandler.current

LaunchedEffect(track) {
viewModel.handleIntent(CommentContract.CommentIntent.Initialize(track))
}
Expand All @@ -60,6 +63,9 @@ fun CommentRoute(
CommentContract.CommentSideEffect.NavigateToHome -> {
navigator.clearAndNavigateTo(Home)
}
is CommentContract.CommentSideEffect.OpenWebView -> {
uriHandler.openUri(sideEffect.url)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.example.comment

import androidx.lifecycle.viewModelScope
import com.example.common.constant.Url
import com.example.domain.repository.PostRepository
import com.example.ui.base.BaseViewModel
import com.example.ui.model.TrackState
Expand Down Expand Up @@ -42,7 +43,7 @@ class CommentViewModel
}
}
CommentContract.CommentIntent.OnMoreGuideClick -> {
// 가이드 노션으로 이동
setSideEffect(CommentContract.CommentSideEffect.OpenWebView(Url.COMMUNITY_GUIDE))
}
CommentContract.CommentIntent.OnRegisterButtonClick -> {
registerPost()
Expand All @@ -59,7 +60,7 @@ class CommentViewModel
track = track,
comment = currentState.commentInput,
).onSuccess {
setSideEffect(CommentContract.CommentSideEffect.NavigateToBack)
setSideEffect(CommentContract.CommentSideEffect.NavigateToHome)
}.onFailure {
}
}
Expand Down
Loading