Skip to content

Commit

Permalink
Merge : 행사 상세 화면 구현
Browse files Browse the repository at this point in the history
* feat : EventDetail Api 구현

* feat : Date Format 함수 구현

* feat : resource 파일 정의

* feat : 상세정보 화면 구현

* refactor : Ktlint 해결

* refactor : EventTag TextView로 변경

* refactor : 안쓰는 함수,변수 삭제 및 ktlint 적용
  • Loading branch information
chws0508 authored Aug 2, 2023
1 parent d804e91 commit 771fff9
Show file tree
Hide file tree
Showing 45 changed files with 796 additions and 64 deletions.
5 changes: 4 additions & 1 deletion android/2023-emmsale/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
<activity
android:name=".presentation.ui.onboarding.OnboardingActivity"
android:exported="true" />
<activity
android:name=".presentation.eventdetail.EventDetailActivity"
android:exported="false" />
<activity
android:name=".presentation.ui.login.LoginActivity"
android:exported="true"
Expand Down Expand Up @@ -53,4 +56,4 @@
</service>
</application>

</manifest>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.emmsale.data.eventdetail

data class EventDetail(
val id: Long,
val name: String,
val status: String,
val location: String,
val startDate: String,
val endDate: String,
val informationUrl: String,
val tags: List<String>,
val imageUrl: String,
val remainingDays: Int,
val type: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.emmsale.data.eventdetail

import com.emmsale.data.common.ApiResult

interface EventDetailRepository {
suspend fun fetchEventDetail(eventId: Long): ApiResult<EventDetail>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.emmsale.data.eventdetail

import com.emmsale.data.common.ApiResult
import com.emmsale.data.common.handleApi
import com.emmsale.data.eventdetail.dto.EventDetailApiModel

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

override suspend fun fetchEventDetail(eventId: Long): ApiResult<EventDetail> {
val response = eventDetailService.fetchEventDetail(eventId)
return handleApi(
response = response,
mapToDomain = EventDetailApiModel::toData,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.emmsale.data.eventdetail

import com.emmsale.data.eventdetail.dto.EventDetailApiModel
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Path

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

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

@Serializable
data class EventDetailApiModel(
@SerialName("id")
val id: Long,
@SerialName("name")
val name: String,
@SerialName("status")
val status: String,
@SerialName("location")
val location: String,
@SerialName("startDate")
val startDate: String,
@SerialName("endDate")
val endDate: String,
@SerialName("informationUrl")
val informationUrl: String,
@SerialName("tags")
val tags: List<String>,
@SerialName("imageUrl")
val imageUrl: String,
@SerialName("remainingDays")
val remainingDays: 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
Expand Up @@ -19,4 +19,4 @@ interface MemberService {

@POST("/members")
suspend fun updateMember(@Body member: MemberApiModel): Response<Unit>
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import com.emmsale.data.activity.ActivityRepository
import com.emmsale.data.activity.ActivityRepositoryImpl
import com.emmsale.data.event.EventRepository
import com.emmsale.data.event.EventRepositoryImpl
import com.emmsale.data.eventdetail.EventDetailRepository
import com.emmsale.data.eventdetail.EventDetailRepositoryImpl
import com.emmsale.data.fcmToken.FcmTokenRepository
import com.emmsale.data.fcmToken.FcmTokenRepositoryImpl
import com.emmsale.data.login.LoginRepository
Expand Down Expand Up @@ -35,4 +37,7 @@ class RepositoryContainer(
val fcmTokenRepository: FcmTokenRepository by lazy {
FcmTokenRepositoryImpl(fcmTokenService = serviceContainer.fcmTokenService)
}
val eventDetailRepository: EventDetailRepository by lazy {
EventDetailRepositoryImpl(eventDetailService = serviceContainer.eventDetailService)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.emmsale.di
import com.emmsale.data.activity.ActivityService
import com.emmsale.data.common.ServiceFactory
import com.emmsale.data.event.EventService
import com.emmsale.data.eventdetail.EventDetailService
import com.emmsale.data.fcmToken.FcmTokenService
import com.emmsale.data.login.LoginService
import com.emmsale.data.member.MemberService
Expand All @@ -13,4 +14,5 @@ class ServiceContainer(serviceFactory: ServiceFactory) {
val memberService: MemberService by lazy { serviceFactory.create(MemberService::class.java) }
val eventService: EventService by lazy { serviceFactory.create(EventService::class.java) }
val fcmTokenService: FcmTokenService by lazy { serviceFactory.create(FcmTokenService::class.java) }
val eventDetailService: EventDetailService by lazy { serviceFactory.create(EventDetailService::class.java) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import com.emmsale.data.common.ServiceFactory
import com.emmsale.di.RepositoryContainer
import com.emmsale.di.ServiceContainer
import com.emmsale.di.SharedPreferenceContainer
import com.emmsale.presentation.common.firebase.analytics.Kerdy.initFirebaseAnalytics
import com.emmsale.presentation.common.firebase.analytics.KerdyAnalytics.initFirebaseAnalytics
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
Expand All @@ -16,7 +16,7 @@ class KerdyApplication : Application() {
super.onCreate()
repositoryContainer = RepositoryContainer(
serviceContainer = ServiceContainer(ServiceFactory()),
preferenceContainer = SharedPreferenceContainer(this)
preferenceContainer = SharedPreferenceContainer(this),
)
applicationScope.launch {
repositoryContainer.tokenRepository.getToken()?.let(::initFirebaseAnalytics)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.emmsale.presentation.common.firebase.analytics
package com.emmsale.presentation.common.firebase.analytics // ktlint-disable filename

import com.emmsale.data.token.Token
import com.google.firebase.analytics.FirebaseAnalytics
Expand All @@ -7,7 +7,7 @@ import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.analytics.ktx.logEvent
import com.google.firebase.ktx.Firebase

object Kerdy {
object KerdyAnalytics {
private val firebaseAnalytics: FirebaseAnalytics = Firebase.analytics

fun initFirebaseAnalytics(token: Token) {
Expand All @@ -18,4 +18,3 @@ object Kerdy {
firebaseAnalytics.logEvent(event, parameters)
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.emmsale.presentation.eventdetail

import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import com.emmsale.databinding.ActivityEventDetailBinding
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)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setUpBinding()
setUpEventDetail()
setBackPress()
viewModel.fetchEventDetail(1)
}

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 = "댓글"
PARTICIPANT_TAB_POSITION -> tab.text = "같이가요"
}
}.attach()
}

private fun setUpBinding() {
binding = ActivityEventDetailBinding.inflate(layoutInflater)
setContentView(binding.root)
}

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 -> showToastMessage("행사 받아오기 실패")
}
}
}

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

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

private fun showToastMessage(message: String) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}

private fun setBackPress() {
binding.ivEventdetailBackpress.setOnClickListener {
finish()
}
}

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 PARTICIPANT_TAB_POSITION = 2

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

import EventParticipantFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.emmsale.presentation.ui.eventdetail.comment.EventCommentFragment
import com.emmsale.presentation.ui.eventdetail.information.EventInfoFragment

class EventDetailFragmentStateAdpater(
fragmentActivity: FragmentActivity,
private val eventId: Long,
private val informationUrl: String,
private val imageUrl: String,
) : FragmentStateAdapter(fragmentActivity) {

override fun getItemCount(): Int = EVENT_DETAIL_TAB_COUNT

override fun createFragment(position: Int): Fragment {
return when (position) {
INFORMATION_TAB -> EventInfoFragment.create(informationUrl, imageUrl)
COMMENT_TAB -> EventCommentFragment.create(eventId)
PARTICIPANT_TAB -> EventParticipantFragment.create(eventId)
else -> throw IllegalArgumentException("알수없는 ViewPager 오류입니다.")
}
}

companion object {
private const val INFORMATION_TAB = 0
private const val COMMENT_TAB = 1
private const val PARTICIPANT_TAB = 2
private const val EVENT_DETAIL_TAB_COUNT = 3
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
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.EventDetailRepository
import com.emmsale.presentation.KerdyApplication
import com.emmsale.presentation.common.ViewModelFactory
import com.emmsale.presentation.ui.eventdetail.uistate.EventDetailUiState
import kotlinx.coroutines.launch

class EventDetailViewModel(
private val eventDetailRepository: EventDetailRepository,
) : ViewModel() {

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

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

is ApiError -> _eventDetail.postValue(EventDetailUiState.Error)
is ApiException -> _eventDetail.postValue(EventDetailUiState.Error)
}
}
}

companion object {
val factory = ViewModelFactory {
EventDetailViewModel(
eventDetailRepository = KerdyApplication.repositoryContainer.eventDetailRepository,
)
}
}
}
Loading

0 comments on commit 771fff9

Please sign in to comment.