feat(chat-original): ChatFragment에 작품별 탭 및 리스트 UI/API 연동 추가
- ChatFragment에 '작품별' 탭 추가 및 프래그먼트 스위칭 로직 반영 - /api/chat/original/list API, 모델, 레포지토리, ViewModel 추가 - OriginalTabFragment/Adapter/레이아웃 구현 (3단 그리드, 간격 16dp, 이미지 라운드 16dp, 아이템 이미지의 레이아웃 비율을 306:432) - 스크롤 끝 감지를 구현하여 무한 스크롤을 지원
This commit is contained in:
		@@ -7,6 +7,7 @@ import com.google.android.material.tabs.TabLayout
 | 
				
			|||||||
import kr.co.vividnext.sodalive.R
 | 
					import kr.co.vividnext.sodalive.R
 | 
				
			||||||
import kr.co.vividnext.sodalive.base.BaseFragment
 | 
					import kr.co.vividnext.sodalive.base.BaseFragment
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.character.CharacterTabFragment
 | 
					import kr.co.vividnext.sodalive.chat.character.CharacterTabFragment
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.original.OriginalTabFragment
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.talk.TalkTabFragment
 | 
					import kr.co.vividnext.sodalive.chat.talk.TalkTabFragment
 | 
				
			||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
 | 
					import kr.co.vividnext.sodalive.common.SharedPreferenceManager
 | 
				
			||||||
import kr.co.vividnext.sodalive.databinding.FragmentChatBinding
 | 
					import kr.co.vividnext.sodalive.databinding.FragmentChatBinding
 | 
				
			||||||
@@ -52,6 +53,7 @@ class ChatFragment : BaseFragment<FragmentChatBinding>(FragmentChatBinding::infl
 | 
				
			|||||||
    private fun setupTabs() {
 | 
					    private fun setupTabs() {
 | 
				
			||||||
        // 탭 추가
 | 
					        // 탭 추가
 | 
				
			||||||
        binding.tabLayout.addTab(binding.tabLayout.newTab().setText("캐릭터"))
 | 
					        binding.tabLayout.addTab(binding.tabLayout.newTab().setText("캐릭터"))
 | 
				
			||||||
 | 
					        binding.tabLayout.addTab(binding.tabLayout.newTab().setText("작품별"))
 | 
				
			||||||
        binding.tabLayout.addTab(binding.tabLayout.newTab().setText("톡"))
 | 
					        binding.tabLayout.addTab(binding.tabLayout.newTab().setText("톡"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 탭 선택 리스너 설정
 | 
					        // 탭 선택 리스너 설정
 | 
				
			||||||
@@ -86,7 +88,8 @@ class ChatFragment : BaseFragment<FragmentChatBinding>(FragmentChatBinding::infl
 | 
				
			|||||||
        // 선택된 탭에 따라 프래그먼트 표시
 | 
					        // 선택된 탭에 따라 프래그먼트 표시
 | 
				
			||||||
        val fragment = when (position) {
 | 
					        val fragment = when (position) {
 | 
				
			||||||
            0 -> CharacterTabFragment()
 | 
					            0 -> CharacterTabFragment()
 | 
				
			||||||
            1 -> TalkTabFragment()
 | 
					            1 -> OriginalTabFragment()
 | 
				
			||||||
 | 
					            2 -> TalkTabFragment()
 | 
				
			||||||
            else -> CharacterTabFragment()
 | 
					            else -> CharacterTabFragment()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.chat.original
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.os.Bundle
 | 
				
			||||||
 | 
					import android.view.View
 | 
				
			||||||
 | 
					import androidx.recyclerview.widget.GridLayoutManager
 | 
				
			||||||
 | 
					import androidx.recyclerview.widget.LinearLayoutManager
 | 
				
			||||||
 | 
					import androidx.recyclerview.widget.RecyclerView
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.base.BaseFragment
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.common.GridSpacingItemDecoration
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.databinding.FragmentOriginalTabBinding
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.extensions.dpToPx
 | 
				
			||||||
 | 
					import org.koin.android.ext.android.inject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OriginalTabFragment :
 | 
				
			||||||
 | 
					    BaseFragment<FragmentOriginalTabBinding>(FragmentOriginalTabBinding::inflate) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val viewModel: OriginalWorkViewModel by inject()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private lateinit var adapter: OriginalWorkListAdapter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 | 
				
			||||||
 | 
					        super.onViewCreated(view, savedInstanceState)
 | 
				
			||||||
 | 
					        setupRecycler()
 | 
				
			||||||
 | 
					        bind()
 | 
				
			||||||
 | 
					        viewModel.loadMore()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun setupRecycler() {
 | 
				
			||||||
 | 
					        val spanCount = 3
 | 
				
			||||||
 | 
					        val spacingPx = 16f.dpToPx().toInt()
 | 
				
			||||||
 | 
					        adapter = OriginalWorkListAdapter { /* TODO: 상세 페이지 이동 정의 시 연결 */ }
 | 
				
			||||||
 | 
					        binding.rvOriginal.layoutManager = GridLayoutManager(requireContext(), spanCount)
 | 
				
			||||||
 | 
					        binding.rvOriginal.addItemDecoration(
 | 
				
			||||||
 | 
					            GridSpacingItemDecoration(
 | 
				
			||||||
 | 
					                spanCount,
 | 
				
			||||||
 | 
					                spacingPx,
 | 
				
			||||||
 | 
					                true
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        binding.rvOriginal.addOnScrollListener(object : RecyclerView.OnScrollListener() {
 | 
				
			||||||
 | 
					            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
 | 
				
			||||||
 | 
					                super.onScrolled(recyclerView, dx, dy)
 | 
				
			||||||
 | 
					                val lastVisibleItemPosition = (recyclerView.layoutManager as LinearLayoutManager)
 | 
				
			||||||
 | 
					                    .findLastVisibleItemPosition()
 | 
				
			||||||
 | 
					                val totalItemCount = recyclerView.adapter?.itemCount ?: 0
 | 
				
			||||||
 | 
					                if (!recyclerView.canScrollVertically(1) && lastVisibleItemPosition >= totalItemCount - 1) {
 | 
				
			||||||
 | 
					                    viewModel.loadMore()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        binding.rvOriginal.adapter = adapter
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun bind() {
 | 
				
			||||||
 | 
					        viewModel.items.observe(viewLifecycleOwner) { list ->
 | 
				
			||||||
 | 
					            // 누적 리스트를 어댑터에 추가
 | 
				
			||||||
 | 
					            adapter.addItems(list.drop(adapter.itemCount))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // 필요 시 로딩/토스트 처리 추가
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.chat.original
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import io.reactivex.rxjava3.core.Single
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.common.ApiResponse
 | 
				
			||||||
 | 
					import retrofit2.http.GET
 | 
				
			||||||
 | 
					import retrofit2.http.Header
 | 
				
			||||||
 | 
					import retrofit2.http.Query
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface OriginalWorkApi {
 | 
				
			||||||
 | 
					    @GET("/api/chat/original/list")
 | 
				
			||||||
 | 
					    fun getOriginalWorkList(
 | 
				
			||||||
 | 
					        @Header("Authorization") authHeader: String,
 | 
				
			||||||
 | 
					        @Query("page") page: Int,
 | 
				
			||||||
 | 
					        @Query("size") size: Int
 | 
				
			||||||
 | 
					    ): Single<ApiResponse<OriginalWorkListResponse>>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.chat.original
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.annotation.SuppressLint
 | 
				
			||||||
 | 
					import android.view.LayoutInflater
 | 
				
			||||||
 | 
					import android.view.ViewGroup
 | 
				
			||||||
 | 
					import androidx.recyclerview.widget.RecyclerView
 | 
				
			||||||
 | 
					import coil.load
 | 
				
			||||||
 | 
					import coil.transform.RoundedCornersTransformation
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.R
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.databinding.ItemOriginalWorkBinding
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.extensions.dpToPx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OriginalWorkListAdapter(
 | 
				
			||||||
 | 
					    private val onClick: (Long) -> Unit
 | 
				
			||||||
 | 
					) : RecyclerView.Adapter<OriginalWorkListAdapter.VH>() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val items = mutableListOf<OriginalWorkListItemResponse>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    inner class VH(val binding: ItemOriginalWorkBinding) : RecyclerView.ViewHolder(binding.root) {
 | 
				
			||||||
 | 
					        fun bind(item: OriginalWorkListItemResponse) {
 | 
				
			||||||
 | 
					            binding.tvTitle.text = item.title
 | 
				
			||||||
 | 
					            binding.tvContentType.text = item.contentType
 | 
				
			||||||
 | 
					            binding.ivCover.load(item.imageUrl) {
 | 
				
			||||||
 | 
					                crossfade(true)
 | 
				
			||||||
 | 
					                placeholder(R.drawable.ic_logo_service_center)
 | 
				
			||||||
 | 
					                transformations(RoundedCornersTransformation(16f.dpToPx()))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            binding.root.setOnClickListener { onClick(item.id) }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
 | 
				
			||||||
 | 
					        val binding = ItemOriginalWorkBinding.inflate(LayoutInflater.from(parent.context), parent, false)
 | 
				
			||||||
 | 
					        return VH(binding)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun getItemCount(): Int = items.size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun onBindViewHolder(holder: VH, position: Int) {
 | 
				
			||||||
 | 
					        holder.bind(items[position])
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressLint("NotifyDataSetChanged")
 | 
				
			||||||
 | 
					    fun submitList(newItems: List<OriginalWorkListItemResponse>) {
 | 
				
			||||||
 | 
					        items.clear()
 | 
				
			||||||
 | 
					        items.addAll(newItems)
 | 
				
			||||||
 | 
					        notifyDataSetChanged()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun addItems(newItems: List<OriginalWorkListItemResponse>) {
 | 
				
			||||||
 | 
					        val start = items.size
 | 
				
			||||||
 | 
					        items.addAll(newItems)
 | 
				
			||||||
 | 
					        notifyItemRangeInserted(start, newItems.size)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressLint("NotifyDataSetChanged")
 | 
				
			||||||
 | 
					    fun clear() {
 | 
				
			||||||
 | 
					        items.clear()
 | 
				
			||||||
 | 
					        notifyDataSetChanged()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.chat.original
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.annotation.Keep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Keep
 | 
				
			||||||
 | 
					data class OriginalWorkListResponse(
 | 
				
			||||||
 | 
					    val totalCount: Long,
 | 
				
			||||||
 | 
					    val content: List<OriginalWorkListItemResponse>
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Keep
 | 
				
			||||||
 | 
					data class OriginalWorkListItemResponse(
 | 
				
			||||||
 | 
					    val id: Long,
 | 
				
			||||||
 | 
					    val imageUrl: String?,
 | 
				
			||||||
 | 
					    val title: String,
 | 
				
			||||||
 | 
					    val contentType: String
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.chat.original
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import io.reactivex.rxjava3.core.Single
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.common.ApiResponse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OriginalWorkRepository(
 | 
				
			||||||
 | 
					    private val api: OriginalWorkApi
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    fun getOriginalWorks(token: String, page: Int, size: Int): Single<ApiResponse<OriginalWorkListResponse>> {
 | 
				
			||||||
 | 
					        return api.getOriginalWorkList(token, page, size)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.chat.original
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.lifecycle.LiveData
 | 
				
			||||||
 | 
					import androidx.lifecycle.MutableLiveData
 | 
				
			||||||
 | 
					import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
 | 
				
			||||||
 | 
					import io.reactivex.rxjava3.schedulers.Schedulers
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.base.BaseViewModel
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.common.SharedPreferenceManager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OriginalWorkViewModel(
 | 
				
			||||||
 | 
					    private val repository: OriginalWorkRepository
 | 
				
			||||||
 | 
					) : BaseViewModel() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val _isLoading = MutableLiveData(false)
 | 
				
			||||||
 | 
					    val isLoading: LiveData<Boolean> get() = _isLoading
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val _toast = MutableLiveData<String?>(null)
 | 
				
			||||||
 | 
					    val toast: LiveData<String?> get() = _toast
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val _totalCount = MutableLiveData<Long>(0)
 | 
				
			||||||
 | 
					    val totalCount: LiveData<Long> get() = _totalCount
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val _items = MutableLiveData<List<OriginalWorkListItemResponse>>(emptyList())
 | 
				
			||||||
 | 
					    val items: LiveData<List<OriginalWorkListItemResponse>> get() = _items
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private var page = 0
 | 
				
			||||||
 | 
					    private val size = 20
 | 
				
			||||||
 | 
					    private var isLast = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun loadMore() {
 | 
				
			||||||
 | 
					        if (_isLoading.value == true || isLast) return
 | 
				
			||||||
 | 
					        _isLoading.value = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        compositeDisposable.add(
 | 
				
			||||||
 | 
					            repository.getOriginalWorks(
 | 
				
			||||||
 | 
					                token = "Bearer ${SharedPreferenceManager.token}",
 | 
				
			||||||
 | 
					                page = page,
 | 
				
			||||||
 | 
					                size = size
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					                .subscribeOn(Schedulers.io())
 | 
				
			||||||
 | 
					                .observeOn(AndroidSchedulers.mainThread())
 | 
				
			||||||
 | 
					                .subscribe({ response ->
 | 
				
			||||||
 | 
					                    val data = response.data
 | 
				
			||||||
 | 
					                    if (response.success && data != null) {
 | 
				
			||||||
 | 
					                        val current = _items.value ?: emptyList()
 | 
				
			||||||
 | 
					                        val next = current + data.content
 | 
				
			||||||
 | 
					                        _items.value = next
 | 
				
			||||||
 | 
					                        _totalCount.value = data.totalCount
 | 
				
			||||||
 | 
					                        if (data.content.isNotEmpty()) {
 | 
				
			||||||
 | 
					                            page += 1
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            isLast = true
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        _toast.value = response.message ?: "알 수 없는 오류가 발생했습니다."
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    _isLoading.value = false
 | 
				
			||||||
 | 
					                }, { e ->
 | 
				
			||||||
 | 
					                    _isLoading.value = false
 | 
				
			||||||
 | 
					                    _toast.value = e.message ?: "알 수 없는 오류가 발생했습니다."
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun refresh() {
 | 
				
			||||||
 | 
					        page = 0
 | 
				
			||||||
 | 
					        isLast = false
 | 
				
			||||||
 | 
					        _items.value = emptyList()
 | 
				
			||||||
 | 
					        loadMore()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -79,6 +79,9 @@ import kr.co.vividnext.sodalive.chat.talk.TalkApi
 | 
				
			|||||||
import kr.co.vividnext.sodalive.chat.talk.TalkTabRepository
 | 
					import kr.co.vividnext.sodalive.chat.talk.TalkTabRepository
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.talk.TalkTabViewModel
 | 
					import kr.co.vividnext.sodalive.chat.talk.TalkTabViewModel
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.talk.room.chatTalkRoomModule
 | 
					import kr.co.vividnext.sodalive.chat.talk.room.chatTalkRoomModule
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.original.OriginalWorkApi
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.original.OriginalWorkRepository
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.original.OriginalWorkViewModel
 | 
				
			||||||
import kr.co.vividnext.sodalive.common.ApiBuilder
 | 
					import kr.co.vividnext.sodalive.common.ApiBuilder
 | 
				
			||||||
import kr.co.vividnext.sodalive.common.ObjectBox
 | 
					import kr.co.vividnext.sodalive.common.ObjectBox
 | 
				
			||||||
import kr.co.vividnext.sodalive.explorer.ExplorerApi
 | 
					import kr.co.vividnext.sodalive.explorer.ExplorerApi
 | 
				
			||||||
@@ -263,6 +266,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
 | 
				
			|||||||
        single { ApiBuilder().build(get(), CharacterApi::class.java) }
 | 
					        single { ApiBuilder().build(get(), CharacterApi::class.java) }
 | 
				
			||||||
        single { ApiBuilder().build(get(), TalkApi::class.java) }
 | 
					        single { ApiBuilder().build(get(), TalkApi::class.java) }
 | 
				
			||||||
        single { ApiBuilder().build(get(), CharacterCommentApi::class.java) }
 | 
					        single { ApiBuilder().build(get(), CharacterCommentApi::class.java) }
 | 
				
			||||||
 | 
					        single { ApiBuilder().build(get(), OriginalWorkApi::class.java) }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val viewModelModule = module {
 | 
					    private val viewModelModule = module {
 | 
				
			||||||
@@ -367,6 +371,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
 | 
				
			|||||||
        viewModel { kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentListViewModel(get()) }
 | 
					        viewModel { kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentListViewModel(get()) }
 | 
				
			||||||
        viewModel { kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentReplyViewModel(get()) }
 | 
					        viewModel { kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentReplyViewModel(get()) }
 | 
				
			||||||
        viewModel { NewCharactersAllViewModel(get()) }
 | 
					        viewModel { NewCharactersAllViewModel(get()) }
 | 
				
			||||||
 | 
					        viewModel { OriginalWorkViewModel(get()) }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val repositoryModule = module {
 | 
					    private val repositoryModule = module {
 | 
				
			||||||
@@ -417,6 +422,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
 | 
				
			|||||||
        factory { TalkTabRepository(get()) }
 | 
					        factory { TalkTabRepository(get()) }
 | 
				
			||||||
        factory { CharacterCommentRepository(get()) }
 | 
					        factory { CharacterCommentRepository(get()) }
 | 
				
			||||||
        factory { NewCharactersRepository(get()) }
 | 
					        factory { NewCharactersRepository(get()) }
 | 
				
			||||||
 | 
					        factory { OriginalWorkRepository(get()) }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										18
									
								
								app/src/main/res/layout/fragment_original_tab.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								app/src/main/res/layout/fragment_original_tab.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
 | 
					    xmlns:tools="http://schemas.android.com/tools"
 | 
				
			||||||
 | 
					    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					    android:layout_height="match_parent">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <androidx.recyclerview.widget.RecyclerView
 | 
				
			||||||
 | 
					        android:id="@+id/rv_original"
 | 
				
			||||||
 | 
					        android:layout_width="0dp"
 | 
				
			||||||
 | 
					        android:layout_height="0dp"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toTopOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintBottom_toBottomOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
					        tools:listitem="@layout/item_original_work" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</androidx.constraintlayout.widget.ConstraintLayout>
 | 
				
			||||||
							
								
								
									
										48
									
								
								app/src/main/res/layout/item_original_work.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								app/src/main/res/layout/item_original_work.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
 | 
					    xmlns:tools="http://schemas.android.com/tools"
 | 
				
			||||||
 | 
					    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					    android:layout_height="wrap_content">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <ImageView
 | 
				
			||||||
 | 
					        android:id="@+id/iv_cover"
 | 
				
			||||||
 | 
					        android:layout_width="0dp"
 | 
				
			||||||
 | 
					        android:layout_height="0dp"
 | 
				
			||||||
 | 
					        android:scaleType="centerCrop"
 | 
				
			||||||
 | 
					        android:contentDescription="@null"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toTopOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintDimensionRatio="306:432"
 | 
				
			||||||
 | 
					        tools:src="@drawable/ic_logo_service_center" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <TextView
 | 
				
			||||||
 | 
					        android:id="@+id/tv_title"
 | 
				
			||||||
 | 
					        android:layout_width="0dp"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:layout_marginTop="6dp"
 | 
				
			||||||
 | 
					        android:ellipsize="end"
 | 
				
			||||||
 | 
					        android:maxLines="1"
 | 
				
			||||||
 | 
					        android:textColor="@color/color_b0bec5"
 | 
				
			||||||
 | 
					        android:textSize="16sp"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toBottomOf="@id/iv_cover"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
					        tools:text="작품 제목" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <TextView
 | 
				
			||||||
 | 
					        android:id="@+id/tv_content_type"
 | 
				
			||||||
 | 
					        android:layout_width="0dp"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:layout_marginTop="2dp"
 | 
				
			||||||
 | 
					        android:ellipsize="end"
 | 
				
			||||||
 | 
					        android:maxLines="1"
 | 
				
			||||||
 | 
					        android:textColor="#78909C"
 | 
				
			||||||
 | 
					        android:textSize="14sp"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toBottomOf="@id/tv_title"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
					        tools:text="Audio Drama" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</androidx.constraintlayout.widget.ConstraintLayout>
 | 
				
			||||||
		Reference in New Issue
	
	Block a user