feat: 메인 라이브

- 신규 UI 적용
This commit is contained in:
2025-07-21 18:00:31 +09:00
parent c55cc68f5c
commit 44d7ce65ae
9 changed files with 104 additions and 61 deletions

View File

@@ -8,6 +8,6 @@ data class GetLatestFinishedLiveResponse(
@SerializedName("memberId") val memberId: Long, @SerializedName("memberId") val memberId: Long,
@SerializedName("nickname") val nickname: String, @SerializedName("nickname") val nickname: String,
@SerializedName("profileImageUrl") val profileImageUrl: String, @SerializedName("profileImageUrl") val profileImageUrl: String,
@SerializedName("title") val title: String, @SerializedName("timeAgo") val timeAgo: String,
@SerializedName("timeAgo") val timeAgo: String @SerializedName("following") val following: Boolean
) )

View File

@@ -50,7 +50,6 @@ class LatestFinishedLiveAdapter(
binding.tvNickname.text = item.nickname binding.tvNickname.text = item.nickname
binding.tvTimeAgo.text = item.timeAgo binding.tvTimeAgo.text = item.timeAgo
binding.tvTitle.text = item.title
binding.root.setOnClickListener { onClick(item.memberId) } binding.root.setOnClickListener { onClick(item.memberId) }
} }
} }

View File

@@ -29,6 +29,7 @@ import kr.co.vividnext.sodalive.live.room.like.GetLiveRoomHeartTotalResponse
import kr.co.vividnext.sodalive.live.room.like.LiveRoomLikeHeartRequest import kr.co.vividnext.sodalive.live.room.like.LiveRoomLikeHeartRequest
import kr.co.vividnext.sodalive.live.room.profile.GetLiveRoomUserProfileResponse import kr.co.vividnext.sodalive.live.room.profile.GetLiveRoomUserProfileResponse
import kr.co.vividnext.sodalive.live.room.tag.GetLiveTagResponse import kr.co.vividnext.sodalive.live.room.tag.GetLiveTagResponse
import kr.co.vividnext.sodalive.settings.ContentType
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.RequestBody import okhttp3.RequestBody
import retrofit2.http.Body import retrofit2.http.Body
@@ -238,4 +239,12 @@ interface LiveApi {
fun getLatestFinishedLive( fun getLatestFinishedLive(
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Flowable<ApiResponse<List<GetLatestFinishedLiveResponse>>> ): Flowable<ApiResponse<List<GetLatestFinishedLiveResponse>>>
@GET("/api/live")
fun getLiveMain(
@Query("timezone") timezone: String,
@Query("contentType") contentType: ContentType,
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
@Header("Authorization") authHeader: String
): Single<ApiResponse<LiveMainResponse>>
} }

View File

@@ -36,7 +36,6 @@ import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCo
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.following.FollowingCreatorActivity import kr.co.vividnext.sodalive.following.FollowingCreatorActivity
import kr.co.vividnext.sodalive.home.AudioContentMainItem
import kr.co.vividnext.sodalive.home.HomeContentAdapter import kr.co.vividnext.sodalive.home.HomeContentAdapter
import kr.co.vividnext.sodalive.live.now.LiveNowAdapter import kr.co.vividnext.sodalive.live.now.LiveNowAdapter
import kr.co.vividnext.sodalive.live.now.all.LiveNowAllActivity import kr.co.vividnext.sodalive.live.now.all.LiveNowAllActivity
@@ -125,7 +124,7 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
setupView() setupView()
message = "라이브를 불러오고 있습니다." message = "라이브를 불러오고 있습니다."
viewModel.getSummary() viewModel.getLiveMain()
} }
override fun onDestroyView() { override fun onDestroyView() {
@@ -211,7 +210,7 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
liveReservationAdapter.clear() liveReservationAdapter.clear()
message = "라이브를 불러오고 있습니다." message = "라이브를 불러오고 있습니다."
viewModel.getSummary() viewModel.getLiveMain()
} }
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
@@ -463,18 +462,7 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
viewModel.replayContentListLiveData.observe(viewLifecycleOwner) { contentList -> viewModel.replayContentListLiveData.observe(viewLifecycleOwner) { contentList ->
if (contentList.isNotEmpty()) { if (contentList.isNotEmpty()) {
adapter.addItems( adapter.addItems(contentList)
contentList.map {
AudioContentMainItem(
contentId = it.contentId,
creatorId = it.creatorId,
title = it.title,
coverImageUrl = it.coverImageUrl,
creatorNickname = it.creatorNickname,
isPointAvailable = it.isPointAvailable
)
}
)
binding.llReplayLive.visibility = View.VISIBLE binding.llReplayLive.visibility = View.VISIBLE
binding.rvReplayLive.visibility = View.VISIBLE binding.rvReplayLive.visibility = View.VISIBLE
@@ -757,7 +745,7 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
private fun startLive(roomId: Long) { private fun startLive(roomId: Long) {
val onEnterRoomSuccess = { val onEnterRoomSuccess = {
viewModel.getSummary() viewModel.getLiveMain()
requireActivity().runOnUiThread { requireActivity().runOnUiThread {
val intent = Intent(requireContext(), LiveRoomActivity::class.java) val intent = Intent(requireContext(), LiveRoomActivity::class.java)
intent.putExtra(Constants.EXTRA_ROOM_ID, roomId) intent.putExtra(Constants.EXTRA_ROOM_ID, roomId)
@@ -785,7 +773,7 @@ class LiveFragment : BaseFragment<FragmentLiveBinding>(FragmentLiveBinding::infl
message = "라이브를 불러오고 있습니다." message = "라이브를 불러오고 있습니다."
liveNowAdapter.clear() liveNowAdapter.clear()
liveReservationAdapter.clear() liveReservationAdapter.clear()
viewModel.getSummary() viewModel.getLiveMain()
} }
}, },
cancelButtonTitle = "닫기", cancelButtonTitle = "닫기",

View File

@@ -0,0 +1,26 @@
package kr.co.vividnext.sodalive.live
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
import kr.co.vividnext.sodalive.home.AudioContentMainItem
import kr.co.vividnext.sodalive.live.recommend.GetRecommendLiveResponse
import kr.co.vividnext.sodalive.live.recommend_channel.GetRecommendChannelResponse
@Keep
data class LiveMainResponse(
@SerializedName("liveOnAirRoomList")
val liveOnAirRoomList: List<GetRoomListResponse>,
@SerializedName("communityPostList")
val communityPostList: List<GetCommunityPostListResponse>,
@SerializedName("recommendLiveList")
val recommendLiveList: List<GetRecommendLiveResponse>,
@SerializedName("latestFinishedLiveList")
val latestFinishedLiveList: List<GetLatestFinishedLiveResponse>,
@SerializedName("replayLive")
val replayLive: List<AudioContentMainItem>,
@SerializedName("followingChannelList")
val followingChannelList: List<GetRecommendChannelResponse>,
@SerializedName("liveReservationRoomList")
val liveReservationRoomList: List<GetRoomListResponse>
)

View File

@@ -19,6 +19,7 @@ import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationResponse
import kr.co.vividnext.sodalive.live.room.kick_out.LiveRoomKickOutRequest import kr.co.vividnext.sodalive.live.room.kick_out.LiveRoomKickOutRequest
import kr.co.vividnext.sodalive.live.room.like.LiveRoomLikeHeartRequest import kr.co.vividnext.sodalive.live.room.like.LiveRoomLikeHeartRequest
import kr.co.vividnext.sodalive.live.room.menu.MenuApi import kr.co.vividnext.sodalive.live.room.menu.MenuApi
import kr.co.vividnext.sodalive.settings.ContentType
import kr.co.vividnext.sodalive.user.CreatorFollowRequestRequest import kr.co.vividnext.sodalive.user.CreatorFollowRequestRequest
import kr.co.vividnext.sodalive.user.UserApi import kr.co.vividnext.sodalive.user.UserApi
import okhttp3.MultipartBody import okhttp3.MultipartBody
@@ -265,4 +266,11 @@ class LiveRepository(
fun getLatestFinishedLive(token: String) = api.getLatestFinishedLive( fun getLatestFinishedLive(token: String) = api.getLatestFinishedLive(
authHeader = token authHeader = token
) )
fun getLiveMain(token: String) = api.getLiveMain(
timezone = TimeZone.getDefault().id,
contentType = ContentType.values()[SharedPreferenceManager.contentPreference],
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
authHeader = token
)
} }

View File

@@ -7,11 +7,11 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.audio_content.AudioContentRepository import kr.co.vividnext.sodalive.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.base.BaseViewModel import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
import kr.co.vividnext.sodalive.home.AudioContentMainItem
import kr.co.vividnext.sodalive.live.recommend.GetRecommendLiveResponse import kr.co.vividnext.sodalive.live.recommend.GetRecommendLiveResponse
import kr.co.vividnext.sodalive.live.recommend.LiveRecommendRepository import kr.co.vividnext.sodalive.live.recommend.LiveRecommendRepository
import kr.co.vividnext.sodalive.live.recommend_channel.GetRecommendChannelResponse import kr.co.vividnext.sodalive.live.recommend_channel.GetRecommendChannelResponse
@@ -64,8 +64,8 @@ class LiveViewModel(
val communityPostItemLiveData: LiveData<List<GetCommunityPostListResponse>> val communityPostItemLiveData: LiveData<List<GetCommunityPostListResponse>>
get() = _communityPostItemLiveData get() = _communityPostItemLiveData
private var _replayContentListLiveData = MutableLiveData<List<GetAudioContentMainItem>>() private var _replayContentListLiveData = MutableLiveData<List<AudioContentMainItem>>()
val replayContentListLiveData: LiveData<List<GetAudioContentMainItem>> val replayContentListLiveData: LiveData<List<AudioContentMainItem>>
get() = _replayContentListLiveData get() = _replayContentListLiveData
private val _latestFinishedLiveListLiveData = private val _latestFinishedLiveListLiveData =
@@ -266,12 +266,6 @@ class LiveViewModel(
_latestFinishedLiveListLiveData.postValue(emptyList()) _latestFinishedLiveListLiveData.postValue(emptyList())
} }
val replayLive = it.replayLive
if (replayLive.success && replayLive.data != null) {
val data = replayLive.data!!
_replayContentListLiveData.postValue(data)
}
_isLoading.postValue(false) _isLoading.postValue(false)
}, },
{ {
@@ -572,4 +566,45 @@ class LiveViewModel(
) )
} }
} }
fun getLiveMain() {
_isLoading.value = true
compositeDisposable.add(
repository.getLiveMain(
"Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success && it.data != null) {
val data = it.data!!
_liveNowLiveData.value = data.liveOnAirRoomList
_recommendLiveData.value = data.recommendLiveList
_liveReservationLiveData.value = data.liveReservationRoomList
_communityPostItemLiveData.value = data.communityPostList
_recommendChannelLiveData.value = data.followingChannelList
_replayContentListLiveData.value = data.replayLive
_latestFinishedLiveListLiveData.value = data.latestFinishedLiveList
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
} }

View File

@@ -321,7 +321,7 @@ class LiveReservationAllActivity : BaseActivity<ActivityLiveReservationAllBindin
private fun onClickStart(roomId: Long) { private fun onClickStart(roomId: Long) {
val onEnterRoomSuccess = { val onEnterRoomSuccess = {
viewModel.getSummary() viewModel.getLiveMain()
runOnUiThread { runOnUiThread {
val intent = Intent(applicationContext, LiveRoomActivity::class.java) val intent = Intent(applicationContext, LiveRoomActivity::class.java)
intent.putExtra(Constants.EXTRA_ROOM_ID, roomId) intent.putExtra(Constants.EXTRA_ROOM_ID, roomId)

View File

@@ -12,25 +12,16 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:padding="16dp"> android:padding="16dp">
<!-- 프로필 이미지 컨테이너 --> <ImageView
<FrameLayout android:id="@+id/iv_profile"
android:id="@+id/fl_profile" android:layout_width="72dp"
android:layout_width="84dp" android:layout_height="72dp"
android:layout_height="84dp" android:layout_gravity="center"
android:contentDescription="@null"
android:scaleType="centerCrop"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent" />
<!-- 프로필 이미지 -->
<ImageView
android:id="@+id/iv_profile"
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_gravity="center"
android:contentDescription="@null"
android:scaleType="centerCrop" />
</FrameLayout>
<LinearLayout <LinearLayout
android:layout_width="0dp" android:layout_width="0dp"
@@ -41,7 +32,7 @@
app:layout_constraintBottom_toTopOf="@+id/tv_time_ago" app:layout_constraintBottom_toTopOf="@+id/tv_time_ago"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fl_profile"> app:layout_constraintTop_toBottomOf="@+id/iv_profile">
<TextView <TextView
android:id="@+id/tv_nickname" android:id="@+id/tv_nickname"
@@ -52,19 +43,6 @@
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="16sp" android:textSize="16sp"
tools:text="도화" /> tools:text="도화" />
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:fontFamily="@font/pretendard_regular"
android:gravity="center"
android:maxLines="1"
android:textColor="#B0BEC5"
android:textSize="12sp"
tools:text="제목제목제목제목제목제목" />
</LinearLayout> </LinearLayout>
<TextView <TextView