-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[feature/semin7]: 7주차 과제제출 #13
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package org.android.go.sopt.data.model.music | ||
|
||
|
||
import kotlinx.serialization.SerialName | ||
import kotlinx.serialization.Serializable | ||
import org.android.go.sopt.domain.entity.music.SoptPostMusicData | ||
|
||
@Serializable | ||
data class SoptPostMusicResponse( | ||
@SerialName("data") | ||
val data: Data? = null, | ||
@SerialName("message") | ||
val message: String? = null, | ||
@SerialName("status") | ||
val status: Int? = null | ||
) { | ||
|
||
@Serializable | ||
data class Data( | ||
@SerialName("singer") | ||
val singer: String? = null, | ||
@SerialName("title") | ||
val title: String? = null, | ||
@SerialName("url") | ||
val url: String? = null | ||
) | ||
} | ||
|
||
fun SoptPostMusicResponse.toSoptPostMusicData() = SoptPostMusicData( | ||
message = message.orEmpty(), | ||
status = status ?: -1, | ||
singer = data?.singer.orEmpty(), | ||
title = data?.title.orEmpty(), | ||
url = data?.url.orEmpty() | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,14 +2,24 @@ package org.android.go.sopt.data.repository | |
|
||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.flow.map | ||
import okhttp3.MultipartBody | ||
import okhttp3.RequestBody | ||
import org.android.go.sopt.data.dao.MusicDao | ||
import org.android.go.sopt.data.model.music.toMusicData | ||
import org.android.go.sopt.data.model.music.toMusicDataEntity | ||
import org.android.go.sopt.domain.repository.MusicRepository | ||
import org.android.go.sopt.data.model.music.toSoptGetMusicData | ||
import org.android.go.sopt.data.model.music.toSoptPostMusicData | ||
import org.android.go.sopt.data.service.sopt.SoptMusicService | ||
import org.android.go.sopt.domain.entity.music.MusicData | ||
import org.android.go.sopt.domain.entity.music.SoptGetMusicData | ||
import org.android.go.sopt.domain.entity.music.SoptPostMusicData | ||
import org.android.go.sopt.domain.repository.MusicRepository | ||
import javax.inject.Inject | ||
|
||
class MusicRepositoryImpl @Inject constructor(private val musicDao: MusicDao) : MusicRepository { | ||
class MusicRepositoryImpl @Inject constructor( | ||
private val musicDao: MusicDao, | ||
private val soptMusicService: SoptMusicService | ||
Comment on lines
+20
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 굳 |
||
) : MusicRepository { | ||
override suspend fun getAll(): Flow<List<MusicData>> { | ||
return musicDao.getAll().map { musicList -> | ||
musicList.map { it.toMusicData() } | ||
|
@@ -31,4 +41,12 @@ class MusicRepositoryImpl @Inject constructor(private val musicDao: MusicDao) : | |
override suspend fun update(musicData: MusicData) { | ||
return musicDao.update(musicData.toMusicDataEntity()) | ||
} | ||
|
||
override suspend fun getMusicList(id: String): Result<SoptGetMusicData> { | ||
return runCatching { soptMusicService.getMusic(id).toSoptGetMusicData() } | ||
} | ||
|
||
override suspend fun postMusic(id: String, image: MultipartBody.Part, title: RequestBody, singer: RequestBody): Result<SoptPostMusicData> { | ||
return runCatching { soptMusicService.postMusic(id, image, title, singer).toSoptPostMusicData() } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package org.android.go.sopt.data.service.sopt | ||
|
||
import okhttp3.MultipartBody | ||
import okhttp3.RequestBody | ||
import org.android.go.sopt.data.model.music.SoptGetMusicListResponse | ||
import org.android.go.sopt.data.model.music.SoptPostMusicResponse | ||
import retrofit2.http.GET | ||
import retrofit2.http.Header | ||
import retrofit2.http.Multipart | ||
import retrofit2.http.POST | ||
import retrofit2.http.Part | ||
import retrofit2.http.Path | ||
|
||
interface SoptMusicService { | ||
|
||
@Multipart | ||
@POST("/music") | ||
suspend fun postMusic( | ||
@Header("id") id: String, | ||
@Part image: MultipartBody.Part, | ||
@Part("title") title: RequestBody, | ||
@Part("singer") singer: RequestBody, | ||
): SoptPostMusicResponse | ||
|
||
@GET("/{id}/music") | ||
suspend fun getMusic( | ||
@Path("id") id: String | ||
): SoptGetMusicListResponse | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package org.android.go.sopt.domain.entity.music | ||
|
||
|
||
import kotlinx.serialization.Serializable | ||
|
||
data class SoptGetMusicData( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://developer.android.com/topic/architecture/data-layer?hl=ko#business-models 이 부분 참고해서 데이터 클래스 네이밍 지어봐도 괜찮을 것 같네요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 감사합니다!! |
||
val message: String, | ||
val status: Int, | ||
val musicList: List<Music> | ||
) { | ||
data class Music( | ||
val singer: String, | ||
val title: String, | ||
val url: String | ||
) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package org.android.go.sopt.domain.entity.music | ||
|
||
import okhttp3.MultipartBody | ||
|
||
data class SoptPostMusicBody( | ||
val singer: String, | ||
val title: String, | ||
val image: MultipartBody.Part | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.android.go.sopt.domain.entity.music | ||
|
||
|
||
|
||
data class SoptPostMusicData( | ||
val message: String, | ||
val status: Int, | ||
val singer: String, | ||
val title: String, | ||
val url: String | ||
) |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,8 @@ | ||||||||||
package org.android.go.sopt.extension | ||||||||||
|
||||||||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull | ||||||||||
import okhttp3.RequestBody.Companion.toRequestBody | ||||||||||
|
||||||||||
class CommonExtension { | ||||||||||
private fun Int.toRequestBody() = toString().toRequestBody("text/plain".toMediaTypeOrNull()) | ||||||||||
} | ||||||||||
Comment on lines
+6
to
+8
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
코틀린이니까 파일안에 함수만 있어도 될듯 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. file로 만든게 아니라 class로 만들었었네요 이런 실수를 ㅋㅎㅋㅎㅋㅎ 감사합니다 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package org.android.go.sopt.presentation.main.home | ||
|
||
import org.android.go.sopt.domain.entity.music.SoptGetMusicData | ||
|
||
sealed class HomeState { | ||
object UnInitialized: HomeState() | ||
|
||
object Loading: HomeState() | ||
|
||
data class SuccessGetMusicList( | ||
val data: SoptGetMusicData | ||
): HomeState() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 의진님은 state 관리 따로, data 관리 따로 하는 것이 좋은 것 같나요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 State 관리와 data 관리를 따로 한다는 관점에 대해서는 깊게 생각해보지 않았는데요. 현재 제가 사용하고 있는 UI State의 사용 이유가 결국 View단에서 State에 따라서만 View가 보여져야하는 형태를 핸들링 할 수 있다는 부분이 유지보수 측면에서 매력적이기 때문입니다. 결국 View가 State에 따라 변경되게 하려면 비즈니스 로직에서 받아오는 data가 State내에 필수적으로 필요할 것 같습니다. 어떻게 보면 MVI의 느낌의 패턴이라고 생각하시면 됩니다. 만약 데이터를 따로 관리하게 된다면, Success인 State에서 data를 따로 observable한 변수를 통해 처리하는 방법을 생각하시는 걸까요?? 이 부분에 있어서 다른 패턴을 적용할 수 있는 예제를 보여주신다면 저도 확인해보겠습니다!! |
||
|
||
object SuccessPostMusic: HomeState() | ||
|
||
object Error: HomeState() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,58 @@ | ||
package org.android.go.sopt.presentation.main.home | ||
|
||
import android.util.Log | ||
import androidx.lifecycle.ViewModel | ||
import androidx.lifecycle.viewModelScope | ||
import dagger.hilt.android.lifecycle.HiltViewModel | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import kotlinx.coroutines.flow.StateFlow | ||
import kotlinx.coroutines.launch | ||
import org.android.go.sopt.domain.entity.reqres.ReqresEntity | ||
import org.android.go.sopt.domain.repository.ReqresRepository | ||
import org.android.go.sopt.presentation.state.UIState | ||
import okhttp3.MediaType.Companion.toMediaType | ||
import okhttp3.MediaType.Companion.toMediaTypeOrNull | ||
import okhttp3.MultipartBody | ||
import okhttp3.RequestBody | ||
import okhttp3.RequestBody.Companion.toRequestBody | ||
import org.android.go.sopt.domain.entity.music.SoptPostMusicBody | ||
import org.android.go.sopt.domain.repository.MusicRepository | ||
import retrofit2.http.Multipart | ||
import javax.inject.Inject | ||
|
||
|
||
@HiltViewModel | ||
class HomeViewModel @Inject constructor(private val reqresRepository: ReqresRepository) : ViewModel() { | ||
class HomeViewModel @Inject constructor( | ||
private val musicRepository: MusicRepository | ||
) : ViewModel() { | ||
|
||
private val _homeStateFlow = MutableStateFlow<HomeState>(HomeState.UnInitialized) | ||
val homeStateFlow: StateFlow<HomeState> get() = _homeStateFlow | ||
|
||
private val _userListStateFlow = MutableStateFlow<UIState<ReqresEntity>>(UIState.UnInitialized) | ||
val userListStateFlow: StateFlow<UIState<ReqresEntity>> get() = _userListStateFlow | ||
private val id = "admin5" | ||
|
||
fun getUsers(page: Int) { | ||
fun getMusicList() { | ||
viewModelScope.launch { | ||
_userListStateFlow.value = UIState.Loading | ||
reqresRepository.getUsers(page)?.let { | ||
_userListStateFlow.value = UIState.Success(it) | ||
} ?: kotlin.run { | ||
_userListStateFlow.value = UIState.Error | ||
} | ||
musicRepository.getMusicList(id) | ||
.onSuccess { | ||
_homeStateFlow.emit(HomeState.SuccessGetMusicList(it)) | ||
}.onFailure { | ||
Log.e("HomeViewModel", "getMusicList() error: $it") | ||
_homeStateFlow.emit(HomeState.Error) | ||
} | ||
} | ||
} | ||
|
||
fun postMusic(soptPostMusicBody: SoptPostMusicBody) { | ||
viewModelScope.launch { | ||
val image = soptPostMusicBody.image | ||
val title = soptPostMusicBody.title.toRequestBody("text/plain".toMediaType()) | ||
val singer = soptPostMusicBody.singer.toRequestBody("text/plain".toMediaType()) | ||
|
||
musicRepository.postMusic(id, image, title, singer) | ||
.onSuccess { | ||
_homeStateFlow.emit(HomeState.SuccessPostMusic) | ||
}.onFailure { | ||
Log.e("HomeViewModel", "postMusic() error: $it") | ||
_homeStateFlow.emit(HomeState.Error) | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SerialName을 사용한다면 변수명을 다른 이름으로 지정해도 되지 않을까요?