Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -2,8 +2,10 @@ package org.sopt.certi.data.remote.datasource

import org.sopt.certi.data.remote.dto.base.ApiResponse
import org.sopt.certi.data.remote.dto.base.NullableApiResponse
import org.sopt.certi.data.remote.dto.request.MajorRequestDto
import org.sopt.certi.data.remote.dto.request.ModifyInterestedJobRequestDto
import org.sopt.certi.data.remote.dto.request.PutPersonalInfoRequestDto
import org.sopt.certi.data.remote.dto.request.UniversityRequestDto
import org.sopt.certi.data.remote.dto.response.GetInterestJobListResponseDto
import org.sopt.certi.data.remote.dto.response.GetPersonalInfoResponseDto
import org.sopt.certi.data.remote.dto.response.GetMyPageResponseDto
Expand All @@ -19,4 +21,6 @@ interface UserRemoteDataSource {
suspend fun getPersonalInfo(): ApiResponse<GetPersonalInfoResponseDto>
suspend fun putPersonalInfo(request: PutPersonalInfoRequestDto): NullableApiResponse<Unit>
suspend fun getPresignedUrl(): ApiResponse<PresignedResponseDto>
suspend fun putUniversity(university: UniversityRequestDto): NullableApiResponse<Unit>
suspend fun putMajor(major: MajorRequestDto): NullableApiResponse<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package org.sopt.certi.data.remote.datasourceimpl
import org.sopt.certi.data.remote.datasource.UserRemoteDataSource
import org.sopt.certi.data.remote.dto.base.ApiResponse
import org.sopt.certi.data.remote.dto.base.NullableApiResponse
import org.sopt.certi.data.remote.dto.request.MajorRequestDto
import org.sopt.certi.data.remote.dto.request.ModifyInterestedJobRequestDto
import org.sopt.certi.data.remote.dto.request.PutPersonalInfoRequestDto
import org.sopt.certi.data.remote.dto.request.UniversityRequestDto
import org.sopt.certi.data.remote.dto.response.GetInterestJobListResponseDto
import org.sopt.certi.data.remote.dto.response.GetMyPageResponseDto
import org.sopt.certi.data.remote.dto.response.GetPersonalInfoResponseDto
Expand Down Expand Up @@ -40,4 +42,10 @@ class UserRemoteDataSourceImpl @Inject constructor(

override suspend fun getPresignedUrl(): ApiResponse<PresignedResponseDto> =
userService.getPresignedUrl()

override suspend fun putUniversity(university: UniversityRequestDto): NullableApiResponse<Unit> =
userService.putUniversity(university)

override suspend fun putMajor(major: MajorRequestDto): NullableApiResponse<Unit> =
userService.putMajor(major)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.sopt.certi.data.remote.dto.request

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

@Serializable
data class MajorRequestDto(
@SerialName("majorName")
val majorName: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.sopt.certi.data.remote.dto.request

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

@Serializable
data class UniversityRequestDto(
@SerialName("universityName")
val universityName: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package org.sopt.certi.data.remote.service

import org.sopt.certi.data.remote.dto.base.ApiResponse
import org.sopt.certi.data.remote.dto.base.NullableApiResponse
import org.sopt.certi.data.remote.dto.request.MajorRequestDto
import org.sopt.certi.data.remote.dto.request.ModifyInterestedJobRequestDto
import org.sopt.certi.data.remote.dto.request.PutPersonalInfoRequestDto
import org.sopt.certi.data.remote.dto.request.UniversityRequestDto
import org.sopt.certi.data.remote.dto.response.GetInterestJobListResponseDto
import org.sopt.certi.data.remote.dto.response.GetMyPageResponseDto
import org.sopt.certi.data.remote.dto.response.GetPersonalInfoResponseDto
Expand Down Expand Up @@ -41,4 +43,10 @@ interface UserService {

@GET("/api/v1/user/presigned-url")
suspend fun getPresignedUrl(): ApiResponse<PresignedResponseDto>

@PUT("/api/v1/user/university")
suspend fun putUniversity(@Body request: UniversityRequestDto): NullableApiResponse<Unit>

@PUT("/api/v1/user/major")
suspend fun putMajor(@Body request: MajorRequestDto): NullableApiResponse<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import org.sopt.certi.data.mapper.todomain.image.toDomain
import org.sopt.certi.data.mapper.todomain.user.toDomain
import org.sopt.certi.data.mapper.todto.user.toDto
import org.sopt.certi.data.remote.datasource.UserRemoteDataSource
import org.sopt.certi.data.remote.dto.request.MajorRequestDto
import org.sopt.certi.data.remote.dto.request.ModifyInterestedJobRequestDto
import org.sopt.certi.data.remote.dto.request.UniversityRequestDto
import org.sopt.certi.data.remote.util.HttpResponseHandler.handleApiResponse
import org.sopt.certi.data.remote.util.HttpResponseHandler.handleNullableApiResponse
import org.sopt.certi.data.remote.util.safeApiCall
Expand Down Expand Up @@ -72,4 +74,16 @@ class UserRepositoryImpl @Inject constructor(
.getOrThrow()
.toDomain()
}

override suspend fun putUniversity(university: String): Result<Unit> = safeApiCall {
userRemoteDataSource.putUniversity(UniversityRequestDto(university))
.handleNullableApiResponse()
.getOrThrow()
}

override suspend fun putMajor(major: String): Result<Unit> = safeApiCall {
userRemoteDataSource.putMajor(MajorRequestDto(major))
.handleNullableApiResponse()
.getOrThrow()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ interface UserRepository {
suspend fun getPersonalInfo(): Result<PersonalInfo>
suspend fun putPersonalInfo(request: PersonalInfo): Result<Unit>
suspend fun getPresignedUrl(): Result<PresignedData>
suspend fun putUniversity(university: String): Result<Unit>
suspend fun putMajor(major: String): Result<Unit>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.sopt.certi.domain.usecase.user

import org.sopt.certi.domain.repository.UserRepository
import javax.inject.Inject

class PutMajorUseCase @Inject constructor(
private val userRepository: UserRepository
) {
suspend operator fun invoke(major: String): Result<Unit> =
userRepository.putMajor(major)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.sopt.certi.domain.usecase.user

import org.sopt.certi.domain.repository.UserRepository
import javax.inject.Inject

class PutUniversityUseCase @Inject constructor(
private val userRepository: UserRepository
) {
suspend operator fun invoke(university: String): Result<Unit> =
userRepository.putUniversity(university)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,43 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.sopt.certi.core.state.UiState
import org.sopt.certi.domain.type.CategoryType
import org.sopt.certi.domain.usecase.auth.SearchMajorUseCase
import org.sopt.certi.domain.usecase.auth.SearchUnivUseCase
import org.sopt.certi.domain.usecase.user.GetInterestedJobListUseCase
import org.sopt.certi.domain.usecase.user.ModifyInterestedJobListUseCase
import org.sopt.certi.presentation.ui.myacademicinfo.state.AcademicUiState
import org.sopt.certi.presentation.ui.myacademicinfo.state.EditSearchUiState
import timber.log.Timber
import javax.inject.Inject

@HiltViewModel
class AcademicInfoViewModel @Inject constructor(
private val searchUnivUseCase: SearchUnivUseCase,
private val searchMajorUseCase: SearchMajorUseCase
private val getInterestedJobListUseCase: GetInterestedJobListUseCase,
private val modifyInterestedJobListUseCase: ModifyInterestedJobListUseCase
) : ViewModel() {
private val _academicUiState = MutableStateFlow(AcademicUiState())
val academicUiState = _academicUiState.asStateFlow()

private val _editUnivUiState = MutableStateFlow(EditSearchUiState())
val editUnivUiState = _editUnivUiState.asStateFlow()

private val _editMajorUiState = MutableStateFlow(EditSearchUiState())
val editMajorUiState = _editMajorUiState.asStateFlow()

private val _editingCategoryList = MutableStateFlow<List<CategoryType>>(emptyList())
val editingCategoryList = _editingCategoryList.asStateFlow()

init {
getJobList()
}

private fun getJobList() = viewModelScope.launch {
getInterestedJobListUseCase()
.onSuccess { resultStringList ->
val categoryList = resultStringList.jobList.mapNotNull { description ->
CategoryType.getByDescription(description)
}
_academicUiState.update { currentState ->
currentState.copy(selectedCategoryList = categoryList)
}
}
.onFailure { error ->
Timber.e(error, "Job List 불러오기 실패")
}
}

fun startCategoryEditing() {
_editingCategoryList.value = _academicUiState.value.selectedCategoryList.toList()
}
Expand All @@ -46,79 +58,16 @@ class AcademicInfoViewModel @Inject constructor(
_editingCategoryList.value = currentList
}

fun saveCategoryChanges() {
_academicUiState.update {
it.copy(selectedCategoryList = _editingCategoryList.value)
}
}

fun getUnivList(univSearchText: String) {
viewModelScope.launch {
_editUnivUiState.update { it.copy(searchListLoadState = UiState.Loading) }
searchUnivUseCase(univSearchText)
.onSuccess { result ->
if (result.isEmpty()) {
_editUnivUiState.update { it.copy(searchListLoadState = UiState.Empty) }
} else {
_editUnivUiState.update { it.copy(searchListLoadState = UiState.Success(result)) }
}
}.onFailure {
_editUnivUiState.update { it.copy(searchListLoadState = UiState.Failure(it.toString())) }
}
}
_editUnivUiState.update { it.copy(searchText = univSearchText) }
}

fun onUnivSearchTextChange(univSearchText: String) {
_editUnivUiState.update { it.copy(searchText = univSearchText) }
if (univSearchText.isBlank()) {
_editUnivUiState.update { it.copy(searchListLoadState = UiState.Init) }
}
}

fun selectUniv(univName: String) {
_editUnivUiState.update {
it.copy(
searchText = univName,
submittedSearchText = univName
)
}
}

fun onUnivSaveClick() {}

fun getMajorList(majorSearchText: String) {
viewModelScope.launch {
_editMajorUiState.update { it.copy(searchListLoadState = UiState.Loading) }
searchMajorUseCase(majorSearchText)
.onSuccess { result ->
if (result.isEmpty()) {
_editMajorUiState.update { it.copy(searchListLoadState = UiState.Empty) }
} else {
_editMajorUiState.update { it.copy(searchListLoadState = UiState.Success(result)) }
}
}.onFailure {
_editMajorUiState.update { it.copy(searchListLoadState = UiState.Failure(it.toString())) }
fun saveCategoryChanges() = viewModelScope.launch {
val request = _editingCategoryList.value.map { it.description }
modifyInterestedJobListUseCase(request)
.onSuccess {
_academicUiState.update {
it.copy(selectedCategoryList = _editingCategoryList.value)
}
}
_editMajorUiState.update { it.copy(searchText = majorSearchText) }
}
.onFailure { error ->
Timber.e(error, "희망분야 재설정 실패")
}
}

fun onMajorSearchTextChange(majorSearchText: String) {
_editMajorUiState.update { it.copy(searchText = majorSearchText) }
if (majorSearchText.isBlank()) {
_editMajorUiState.update { it.copy(searchListLoadState = UiState.Init) }
}
}

fun selectMajor(majorName: String) {
_editMajorUiState.update {
it.copy(
searchText = majorName,
submittedSearchText = majorName
)
}
}

fun onMajorSaveClick() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.sopt.certi.presentation.ui.myacademicinfo

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.sopt.certi.core.state.UiState
import org.sopt.certi.domain.usecase.auth.SearchMajorUseCase
import org.sopt.certi.domain.usecase.user.PutMajorUseCase
import org.sopt.certi.presentation.ui.myacademicinfo.state.EditSearchUiState
import timber.log.Timber
import javax.inject.Inject

@HiltViewModel
class EditMajorViewModel @Inject constructor(
private val searchMajorUseCase: SearchMajorUseCase,
private val putMajorUseCase: PutMajorUseCase
) : ViewModel() {
private val _editMajorUiState = MutableStateFlow(EditSearchUiState())
val editMajorUiState = _editMajorUiState.asStateFlow()

fun getMajorList(majorSearchText: String) {
viewModelScope.launch {
_editMajorUiState.update { it.copy(searchListLoadState = UiState.Loading) }
searchMajorUseCase(majorSearchText)
.onSuccess { result ->
if (result.isEmpty()) {
_editMajorUiState.update { it.copy(searchListLoadState = UiState.Empty) }
} else {
_editMajorUiState.update { it.copy(searchListLoadState = UiState.Success(result)) }
}
}.onFailure {
_editMajorUiState.update { it.copy(searchListLoadState = UiState.Failure(it.toString())) }
}
}
_editMajorUiState.update { it.copy(searchText = majorSearchText) }
}

fun onMajorSearchTextChange(majorSearchText: String) {
_editMajorUiState.update { it.copy(searchText = majorSearchText) }
if (majorSearchText.isBlank()) {
_editMajorUiState.update { it.copy(searchListLoadState = UiState.Init) }
}
}

fun selectMajor(majorName: String) {
_editMajorUiState.update {
it.copy(
searchText = majorName,
submittedSearchText = majorName
)
}
}

fun onMajorSaveClick() = viewModelScope.launch {
val submittedText = _editMajorUiState.value.submittedSearchText
putMajorUseCase(submittedText)
.onSuccess {
_editMajorUiState.update {
it.copy(
initialValue = submittedText
)
}
}
.onFailure { error ->
Timber.e(error, "학과 변경 실패")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import org.sopt.certi.ui.theme.CertiTheme
@Composable
fun EditUnivRoute(
padding: PaddingValues,
viewModel: AcademicInfoViewModel = hiltViewModel()
viewModel: EditUnivViewModel = hiltViewModel()
) {
val uiState by viewModel.editUnivUiState.collectAsStateWithLifecycle()

Expand All @@ -55,7 +55,7 @@ fun EditUnivRoute(
@Composable
fun EditMajorRoute(
padding: PaddingValues,
viewModel: AcademicInfoViewModel = hiltViewModel()
viewModel: EditMajorViewModel = hiltViewModel()
) {
val uiState by viewModel.editMajorUiState.collectAsStateWithLifecycle()

Expand Down
Loading