Skip to content

Commit

Permalink
refactor : EventDetail 코드 리팩토링 (#282)
Browse files Browse the repository at this point in the history
* refactor :  EventDetail 코드 리팩토링

* refactor :  EventDetail 코드 리팩토링

* refactor :  EventDetail 코드 리팩토링

* refactor :  EventDetail Mapper 분리

* refactor : 버튼 클릭 함수명 변경

* refactor : viewModel 생성시에 한번만 fetch 하도록 수정

* refactor : mapper 분리에 대한 RepositoryImpl 변경사항 적용

* refactor : init 함수와 프로퍼티 순서 바꿈

* refactor : 부나 피드백 반영

- 함수명 변경
- strings.xml 포맷 변경
- initFragmentStateAdapter 리스트 생성하여 attach 하도록 변경
- imageUrl 을 postImageUrl 로 좀더 명확하게 변경

* refactor : EventDetailApiModel 속성 변경
  • Loading branch information
chws0508 authored Aug 11, 2023
1 parent e512c1b commit c53f59e
Show file tree
Hide file tree
Showing 14 changed files with 155 additions and 123 deletions.
2 changes: 1 addition & 1 deletion android/2023-emmsale/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
android:name=".presentation.ui.onboarding.OnboardingActivity"
android:exported="true" />
<activity
android:name=".presentation.eventdetail.EventDetailActivity"
android:name=".presentation.ui.eventdetail.EventDetailActivity"
android:exported="false" />
<activity
android:name=".presentation.ui.login.LoginActivity"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package com.emmsale.data.eventdetail

import java.time.LocalDateTime

data class EventDetail(
val id: Long,
val name: String,
val status: String,
val location: String,
val startDate: String,
val endDate: String,
val startDate: LocalDateTime,
val endDate: LocalDateTime,
val informationUrl: String,
val tags: List<String>,
val imageUrl: String?,
val postImageUrl: String?,
val remainingDays: Int,
val type: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package com.emmsale.data.eventdetail
import com.emmsale.data.common.ApiResult

interface EventDetailRepository {
suspend fun fetchEventDetail(eventId: Long): ApiResult<EventDetail>
suspend fun getEventDetail(eventId: Long): ApiResult<EventDetail>
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ package com.emmsale.data.eventdetail
import com.emmsale.data.common.ApiResult
import com.emmsale.data.common.handleApi
import com.emmsale.data.eventdetail.dto.EventDetailApiModel
import com.emmsale.data.eventdetail.mapper.toData

class EventDetailRepositoryImpl(
private val eventDetailService: EventDetailService,
) : EventDetailRepository {

override suspend fun fetchEventDetail(eventId: Long): ApiResult<EventDetail> {
override suspend fun getEventDetail(eventId: Long): ApiResult<EventDetail> {
return handleApi(
execute = { eventDetailService.fetchEventDetail(eventId) },
execute = { eventDetailService.getEventDetail(eventId) },
mapToDomain = EventDetailApiModel::toData,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import retrofit2.http.Path

interface EventDetailService {
@GET("events/{eventId}")
suspend fun fetchEventDetail(
suspend fun getEventDetail(
@Path("eventId") eventId: Long,
): Response<EventDetailApiModel>
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.emmsale.data.eventdetail.dto

import com.emmsale.data.eventdetail.EventDetail
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

Expand All @@ -10,36 +9,30 @@ data class EventDetailApiModel(
val id: Long,
@SerialName("name")
val name: String,
@SerialName("status")
val status: String,
@SerialName("location")
val location: String,
@SerialName("informationUrl")
val informationUrl: String,
@SerialName("startDate")
val startDate: String,
@SerialName("endDate")
val endDate: String,
@SerialName("informationUrl")
val informationUrl: String,
@SerialName("applyStartDate")
val applyStartDate: String,
@SerialName("applyEndDate")
val applyEndDate: String,
@SerialName("location")
val location: String,
@SerialName("status")
val status: String,
@SerialName("applyStatus")
val applyStatus: String,
@SerialName("tags")
val tags: List<String>,
@SerialName("imageUrl")
val imageUrl: String?,
@SerialName("remainingDays")
val remainingDays: Int,
@SerialName("applyRemainingDays")
val applyRemainingDays: Int,
@SerialName("type")
val type: String,
) {
fun toData(): EventDetail = EventDetail(
id = id,
name = name,
status = status,
location = location,
startDate = startDate,
endDate = endDate,
informationUrl = informationUrl,
tags = tags,
imageUrl = imageUrl,
remainingDays = remainingDays,
type = type,
)
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.emmsale.data.eventdetail.mapper

import com.emmsale.data.eventdetail.EventDetail
import com.emmsale.data.eventdetail.dto.EventDetailApiModel
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

fun EventDetailApiModel.toData(): EventDetail = EventDetail(
id = id,
name = name,
status = status,
location = location,
startDate = startDate.toLocalDateTime(),
endDate = endDate.toLocalDateTime(),
informationUrl = informationUrl,
tags = tags,
postImageUrl = imageUrl,
remainingDays = remainingDays,
type = type,
)

private fun String.toLocalDateTime(): LocalDateTime {
val format = DateTimeFormatter.ofPattern("yyyy:MM:dd:HH:mm:ss")
return LocalDateTime.parse(this, format)
}
Original file line number Diff line number Diff line change
@@ -1,77 +1,72 @@
package com.emmsale.presentation.eventdetail
package com.emmsale.presentation.ui.eventdetail

import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import com.emmsale.R
import com.emmsale.databinding.ActivityEventDetailBinding
import com.emmsale.presentation.common.extension.showToast
import com.emmsale.presentation.ui.eventdetail.EventDetailFragmentStateAdpater
import com.emmsale.presentation.ui.eventdetail.EventDetailViewModel
import com.emmsale.presentation.ui.eventdetail.EventTag
import com.emmsale.presentation.ui.eventdetail.uistate.EventDetailUiState
import com.google.android.material.tabs.TabLayoutMediator

class EventDetailActivity : AppCompatActivity() {
private lateinit var binding: ActivityEventDetailBinding
private val viewModel: EventDetailViewModel by viewModels { EventDetailViewModel.factory }
private val eventId: Long by lazy {
intent.getLongExtra(EVENT_ID_KEY, DEFAULT_EVENT_ID)
}

private val viewModel: EventDetailViewModel by viewModels { EventDetailViewModel.factory(eventId) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setUpBinding()
setUpEventDetail()
setBackPress()
viewModel.fetchEventDetail(eventId)
initBackPressButtonClickListener()
}

private fun setUpBinding() {
binding = ActivityEventDetailBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.lifecycleOwner = this
binding.vm = viewModel
}

private fun setUpEventDetail() {
viewModel.eventDetail.observe(this) { eventDetailUiState ->
when (eventDetailUiState) {
is EventDetailUiState.Success -> {
binding.eventDetail = eventDetailUiState
addTag(eventDetailUiState.tags)
initFragmentStateAdapter(
eventDetailUiState.informationUrl,
eventDetailUiState.imageUrl,
)
}

else -> showToast("행사 받아오기 실패")
if (eventDetailUiState.isError) {
showToast(getString(R.string.eventdetail_fetch_eventdetail_error_message))
} else {
addEventTag(eventDetailUiState.tags)
initFragmentStateAdapter(
eventDetailUiState.informationUrl,
eventDetailUiState.imageUrl,
)
}
}
}

private fun initFragmentStateAdapter(informationUrl: String, imageUrl: String?) {
binding.vpEventdetail.adapter =
EventDetailFragmentStateAdpater(this, eventId, informationUrl, imageUrl)
TabLayoutMediator(binding.tablayoutEventdetail, binding.vpEventdetail) { tab, position ->
when (position) {
INFORMATION_TAB_POSITION -> tab.text = "상세 정보"
COMMENT_TAB_POSITION -> tab.text = "댓글"
RECRUITMENT_TAB_POSITION -> tab.text = "같이가요"
}
val tabNames = listOf(
getString(R.string.eventdetail_tab_infromation),
getString(R.string.eventdetail_tab_comment),
getString(R.string.eventdetail_tab_recruitment),
)
TabLayoutMediator(binding.tablayoutEventdetail, binding.vpEventdetail) { _, _ ->
tabNames
}.attach()
binding.vpEventdetail.isUserInputEnabled = false
}

private fun addTag(tags: List<String>) {
tags.forEach { binding.chipgroupEvendetailTags.addView(createTag(it)) }
private fun addEventTag(tags: List<String>) {
tags.forEach { binding.chipgroupEvendetailTags.addView(createEventTag(it)) }
}

private fun createTag(tag: String) = EventTag(this).apply {
private fun createEventTag(tag: String) = EventTag(this).apply {
text = tag
}

private fun setBackPress() {
private fun initBackPressButtonClickListener() {
binding.ivEventdetailBackpress.setOnClickListener {
finish()
}
Expand All @@ -80,10 +75,6 @@ class EventDetailActivity : AppCompatActivity() {
companion object {
private const val EVENT_ID_KEY = "EVENT_ID_KEY"
private const val DEFAULT_EVENT_ID = 1L
private const val INFORMATION_TAB_POSITION = 0
private const val COMMENT_TAB_POSITION = 1
private const val RECRUITMENT_TAB_POSITION = 2

fun startActivity(context: Context, eventId: Long) {
val intent = Intent(context, EventDetailActivity::class.java)
intent.putExtra(EVENT_ID_KEY, eventId)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,63 @@
package com.emmsale.presentation.ui.eventdetail

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.emmsale.data.common.ApiError
import com.emmsale.data.common.ApiException
import com.emmsale.data.common.ApiSuccess
import com.emmsale.data.eventdetail.EventDetail
import com.emmsale.data.eventdetail.EventDetailRepository
import com.emmsale.data.member.MemberRepository
import com.emmsale.presentation.KerdyApplication
import com.emmsale.presentation.common.ViewModelFactory
import com.emmsale.presentation.common.livedata.NotNullLiveData
import com.emmsale.presentation.common.livedata.NotNullMutableLiveData
import com.emmsale.presentation.ui.eventdetail.uistate.EventDetailUiState
import kotlinx.coroutines.launch

class EventDetailViewModel(
private val eventId: Long,
private val eventDetailRepository: EventDetailRepository,
private val memberRepository: MemberRepository,
) : ViewModel() {

private val _eventDetail: MutableLiveData<EventDetailUiState> =
MutableLiveData<EventDetailUiState>()
val eventDetail: LiveData<EventDetailUiState>
get() = _eventDetail
private val _eventDetail: NotNullMutableLiveData<EventDetailUiState> =
NotNullMutableLiveData(EventDetailUiState())
val eventDetail: NotNullLiveData<EventDetailUiState> = _eventDetail

fun fetchEventDetail(id: Long) {
viewModelScope.launch {
when (val result = eventDetailRepository.fetchEventDetail(id)) {
is ApiSuccess -> _eventDetail.postValue(
EventDetailUiState.from(result.data),
)
init {
fetchEventDetail(eventId)
}

is ApiError -> _eventDetail.postValue(EventDetailUiState.Error)
is ApiException -> _eventDetail.postValue(EventDetailUiState.Error)
private fun fetchEventDetail(id: Long) {
setLoadingState(true)
viewModelScope.launch {
when (val result = eventDetailRepository.getEventDetail(id)) {
is ApiSuccess -> fetchSuccessEventDetail(result.data)
is ApiError -> changeToErrorState()
is ApiException -> changeToErrorState()
}
}
}

private fun setLoadingState(state: Boolean) {
_eventDetail.value = _eventDetail.value.copy(isLoading = state)
}

private fun fetchSuccessEventDetail(eventDetail: EventDetail) {
_eventDetail.value = EventDetailUiState.from(eventDetail)
}

private fun changeToErrorState() {
_eventDetail.value = _eventDetail.value.copy(
isError = true,
isLoading = false,
)
}

companion object {
val factory = ViewModelFactory {
fun factory(eventId: Long) = ViewModelFactory {
EventDetailViewModel(
eventId,
eventDetailRepository = KerdyApplication.repositoryContainer.eventDetailRepository,
memberRepository = KerdyApplication.repositoryContainer.memberRepository,
)
}
}
Expand Down
Loading

0 comments on commit c53f59e

Please sign in to comment.