Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
15e5fa1
[CHORE]: 동아리 번호 확인 dto 작성
casper-jr Oct 3, 2025
fde50f8
[CHORE]: 동아리 추가 dto 작성
casper-jr Oct 3, 2025
a1c8c25
[CHORE]: 동아리 추가 화면 viewmodel 기본 틀 작성
casper-jr Oct 3, 2025
d2e3267
[CHORE]: datasource에 동아리 추가, 동아리 코드 확인 함수 작성
casper-jr Oct 3, 2025
d8676dc
[CHORE]: repository에 동아리 추가, 동아리 코드 확인 함수 작성
casper-jr Oct 3, 2025
a08c50a
[CHORE]: di 모듈에 addclubviewmodel 추가
casper-jr Oct 3, 2025
7ee58f5
[CHORE]: 동아리 번호 확인 함수 구현
casper-jr Oct 3, 2025
7ce62f5
[FEAT]: 동아리 추가 함수 구현
casper-jr Oct 3, 2025
ef825a4
[FEAT]: Screen에서 uiState 사용하도록 리팩토링
casper-jr Oct 3, 2025
777a26d
[REFACTOR]: currentState에 uiState의 값을 사용하도록 리팩토링
casper-jr Oct 3, 2025
5238962
[CHORE]: API 응답 실패시 메세지 사용을 위한 공용 dto 추가
casper-jr Oct 3, 2025
3c6a24e
[REFACTOR]: 동아리 번호 확인 요청 실패시 메세지 표시를 위한 응답 처리
casper-jr Oct 3, 2025
de31654
[UI]: 동아리 번호 확인 요청 오류 메세지를 ui에 반영
casper-jr Oct 3, 2025
a218892
[CHORE]: 동아리 추가 응답 실패시 파싱 처리
casper-jr Oct 3, 2025
f68a663
[FEAT]: 동아리 추가 응답 여부를 위한 변수를 uistate에 추가
casper-jr Oct 3, 2025
481752b
[FEAT]: 동아리 추가 실패시 오류 메세지를 화면에 출력
casper-jr Oct 3, 2025
f626ac2
[FEAT]: 동아리 조회 성공시 키보드 숨김 기능 추가
casper-jr Oct 3, 2025
16e9264
[FIX]: 동아리 조회 성공시 키보드 숨김 로직 수정
casper-jr Oct 3, 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
@@ -0,0 +1,16 @@
package org.whosin.client.data.dto.response

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class AddClubResponseDto(
@SerialName("success")
val success: Boolean,
@SerialName("status")
val status: Int,
@SerialName("message")
val message: String,
@SerialName("data")
val data: String? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.whosin.client.data.dto.response

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ClubCodeConfirmResponseDto(
@SerialName("success")
val success: Boolean,
@SerialName("status")
val status: Int,
@SerialName("message")
val message: String,
@SerialName("data")
val data: ClubCodeConfirmData
)

@Serializable
data class ClubCodeConfirmData(
@SerialName("clubId")
val clubId: Int,
@SerialName("clubName")
val clubName: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.whosin.client.data.dto.response

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ErrorResponseDto(
@SerialName("success")
val success: Boolean,
@SerialName("status")
val status: Int,
@SerialName("message")
val message: String,
@SerialName("timestamp")
val timestamp: String? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.delete
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import io.ktor.client.request.post
import io.ktor.client.statement.HttpResponse
import io.ktor.http.isSuccess
import io.ktor.http.parameters
import org.whosin.client.core.network.ApiResult
import org.whosin.client.data.dto.response.AddClubResponseDto
import org.whosin.client.data.dto.response.ClubCodeConfirmResponseDto
import org.whosin.client.data.dto.response.ClubPresencesResponseDto
import org.whosin.client.data.dto.response.ErrorResponseDto
import org.whosin.client.data.dto.response.MyClubResponseDto

class RemoteClubDataSource(
Expand Down Expand Up @@ -93,4 +98,68 @@ class RemoteClubDataSource(
ApiResult.Error(message = t.message, cause = t)
}
}

// 동아리 번호 확인
suspend fun confirmClubCode(clubCode: String): ApiResult<ClubCodeConfirmResponseDto>{
return try {
val response: HttpResponse = client.get(urlString = "clubs"){
parameter("clubNumber", clubCode)
}

if (response.status.isSuccess()) {
ApiResult.Success(
data = response.body(),
statusCode = response.status.value
)
} else {
// 에러 응답 파싱 시도
try {
val errorResponse: ErrorResponseDto = response.body()
ApiResult.Error(
code = response.status.value,
message = errorResponse.message
)
} catch (e: Exception) {
// 파싱 실패 시 기본 에러 메시지
ApiResult.Error(
code = response.status.value,
message = "HTTP Error: ${response.status.value}"
)
}
}
} catch (t: Throwable){
ApiResult.Error(message = t.message, cause = t)
}
}

// 동아리 추가 함수
suspend fun addClub(clubId: Int): ApiResult<AddClubResponseDto> {
return try {
val response: HttpResponse = client.post(urlString = "clubs/$clubId")

if (response.status.isSuccess()) {
ApiResult.Success(
data = response.body(),
statusCode = response.status.value
)
} else {
// 에러 응답 파싱 시도
try {
val errorResponse: ErrorResponseDto = response.body()
ApiResult.Error(
code = response.status.value,
message = errorResponse.message
)
} catch (e: Exception) {
// 파싱 실패 시 기본 에러 메시지
ApiResult.Error(
code = response.status.value,
message = "HTTP Error: ${response.status.value}"
)
}
}
} catch (t: Throwable){
ApiResult.Error(message = t.message, cause = t)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.whosin.client.data.repository

import org.whosin.client.core.network.ApiResult
import org.whosin.client.data.dto.response.AddClubResponseDto
import org.whosin.client.data.dto.response.ClubCodeConfirmResponseDto
import org.whosin.client.data.dto.response.ClubPresencesResponseDto
import org.whosin.client.data.dto.response.MyClubResponseDto
import org.whosin.client.data.remote.RemoteClubDataSource
Expand All @@ -19,4 +21,10 @@ class ClubRepository(

suspend fun checkOut(clubId: Int): ApiResult<Unit> =
dataSource.checkOut(clubId)

suspend fun confirmClubCode(clubCode: String): ApiResult<ClubCodeConfirmResponseDto> =
dataSource.confirmClubCode(clubCode)

suspend fun addClub(clubId: Int): ApiResult<AddClubResponseDto> =
dataSource.addClub(clubId)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import org.whosin.client.data.remote.RemoteMemberDataSource
import org.whosin.client.data.repository.DummyRepository
import org.whosin.client.data.repository.ClubRepository
import org.whosin.client.data.repository.MemberRepository
import org.whosin.client.presentation.auth.clubcode.AddClubViewModel
import org.whosin.client.presentation.dummy.DummyViewModel
import org.whosin.client.presentation.dummy.TokenTestViewModel
import org.whosin.client.presentation.auth.login.viewmodel.LoginViewModel
Expand Down Expand Up @@ -49,4 +50,5 @@ val viewModelModule = module {
viewModelOf(::MyPageViewModel)
viewModelOf(::DummyViewModel) // TODO: 이후에 삭제 예정
viewModelOf(::TokenTestViewModel) // TODO: 이후에 삭제 예정
viewModelOf(::AddClubViewModel)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package org.whosin.client.presentation.auth.clubcode

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.whosin.client.core.network.ApiResult
import org.whosin.client.data.repository.ClubRepository

data class AddClubUiState(
val isLoading: Boolean = false,
val verificationState: ClubCodeState = ClubCodeState.INPUT,
val clubName: String? = null,
val clubId: Int? = null,
val errorMessage: String? = null,
val isAddClubSuccess: Boolean = false
)

class AddClubViewModel(
private val repository: ClubRepository
) : ViewModel() {
private val _uiState = MutableStateFlow(AddClubUiState())
val uiState: StateFlow<AddClubUiState> = _uiState.asStateFlow()

fun confirmClubCode(clubCode: String) {
viewModelScope.launch {
_uiState.update { it.copy(isLoading = true) }
when (val result = repository.confirmClubCode(clubCode = clubCode)) {
is ApiResult.Success -> {
val response = result.data.data
_uiState.update {
it.copy(
isLoading = false,
verificationState = ClubCodeState.SUCCESS,
clubName = response.clubName,
clubId = response.clubId,
errorMessage = null
)
}
println("AddClubViewModel : 조회 성공")
}
is ApiResult.Error -> {
_uiState.update {
it.copy(
isLoading = false,
verificationState = ClubCodeState.ERROR,
errorMessage = result.message?: "동아리 이름 조회에 오류가 발생했습니다."
)
}
println("AddClubViewModel : 조회 실패")
}
}
}
}

// 에러 상태 리셋 함수
fun resetErrorState() {
_uiState.update {
it.copy(
verificationState = ClubCodeState.INPUT,
errorMessage = null,
clubName = null,
clubId = null
)
}
}

// 동아리 추가 함수
fun addClub(clubId: Int) {
viewModelScope.launch {
_uiState.update { it.copy(isLoading = true) }
when (val result = repository.addClub(clubId = clubId)) {
is ApiResult.Success -> {
_uiState.update {
it.copy(
isLoading = false,
errorMessage = null,
isAddClubSuccess = true
)
}
}
is ApiResult.Error -> {
_uiState.update {
it.copy(
isLoading = false,
errorMessage = result.message ?: "동아리 추가에 오류가 발생했습니다."
)
}
}
}
}
}
}
Loading