diff --git a/core/designsystem/src/main/res/drawable/ic_arrow_black_svg.xml b/core/designsystem/src/main/res/drawable/ic_arrow_black_svg.xml new file mode 100644 index 00000000..6b5550e9 --- /dev/null +++ b/core/designsystem/src/main/res/drawable/ic_arrow_black_svg.xml @@ -0,0 +1,13 @@ + + + diff --git a/feature/clip/src/main/res/drawable/shape_grey900_fill_8_rect.xml b/core/designsystem/src/main/res/drawable/shape_grey900_fill_8_rect.xml similarity index 100% rename from feature/clip/src/main/res/drawable/shape_grey900_fill_8_rect.xml rename to core/designsystem/src/main/res/drawable/shape_grey900_fill_8_rect.xml diff --git a/feature/clip/src/main/res/drawable/shape_red_fill_8_rect.xml b/core/designsystem/src/main/res/drawable/shape_red_fill_8_rect.xml similarity index 100% rename from feature/clip/src/main/res/drawable/shape_red_fill_8_rect.xml rename to core/designsystem/src/main/res/drawable/shape_red_fill_8_rect.xml diff --git a/core/designsystem/src/main/res/values/strings.xml b/core/designsystem/src/main/res/values/strings.xml index c4f23723..74c58131 100644 --- a/core/designsystem/src/main/res/values/strings.xml +++ b/core/designsystem/src/main/res/values/strings.xml @@ -152,9 +152,11 @@ 잊지 않고 읽었어요! nn /nn - 님의 클립 + 님이 최근 저장한 링크 이주의 링크 이주의 추천 사이트 + 첫번째 링크를 저장해보세요 + 1분 설문조사 참여하고\n스타벅스 기프티콘 받기 토스터 사용 피드백을 남겨주시면\n추첨을 통해 기프티콘을 드려요! 참여하기 diff --git a/core/model/src/main/java/org/sopt/model/home/RecentSavedLink.kt b/core/model/src/main/java/org/sopt/model/home/RecentSavedLink.kt new file mode 100644 index 00000000..5e384464 --- /dev/null +++ b/core/model/src/main/java/org/sopt/model/home/RecentSavedLink.kt @@ -0,0 +1,10 @@ +package org.sopt.model.home + +data class RecentSavedLink( + val toastId: Long, + val toastTitle: String, + val linkUrl: String, + val isRead: Boolean, + val categoryTitle: String?, + val thumbnailUrl: String?, +) diff --git a/data-remote/home/src/main/java/org/sopt/remote/home/api/HomeService.kt b/data-remote/home/src/main/java/org/sopt/remote/home/api/HomeService.kt index 8c9fc520..518753e8 100644 --- a/data-remote/home/src/main/java/org/sopt/remote/home/api/HomeService.kt +++ b/data-remote/home/src/main/java/org/sopt/remote/home/api/HomeService.kt @@ -2,6 +2,7 @@ package org.sopt.remote.home.api import org.sopt.network.model.response.base.BaseResponse import org.sopt.remote.home.response.ResponseMainPageDto +import org.sopt.remote.home.response.ResponseRecentSavedLinkDto import org.sopt.remote.home.response.ResponseRecommendLinkDto import org.sopt.remote.home.response.ResponseWeekBestLinkDto import retrofit2.http.GET @@ -13,6 +14,7 @@ interface HomeService { const val SITES = "sites" const val TOAST = "toast" const val WEEK = "week" + const val RECENT_SAVED = "recent-saved" } @GET("/$USER/$MAIN") @@ -23,4 +25,7 @@ interface HomeService { @GET("/$TOAST/$WEEK") suspend fun getWeekBestLink(): BaseResponse> + + @GET("/$TOAST/$RECENT_SAVED") + suspend fun getRecentSavedLink(): BaseResponse> } diff --git a/data-remote/home/src/main/java/org/sopt/remote/home/datasource/RemoteHomeDataSourceImpl.kt b/data-remote/home/src/main/java/org/sopt/remote/home/datasource/RemoteHomeDataSourceImpl.kt index ebe14cf7..6169c216 100644 --- a/data-remote/home/src/main/java/org/sopt/remote/home/datasource/RemoteHomeDataSourceImpl.kt +++ b/data-remote/home/src/main/java/org/sopt/remote/home/datasource/RemoteHomeDataSourceImpl.kt @@ -2,6 +2,7 @@ package org.sopt.remote.home.datasource import org.sopt.home.datasource.RemoteHomeDataSource import org.sopt.model.home.MainPageData +import org.sopt.model.home.RecentSavedLink import org.sopt.model.home.RecommendLink import org.sopt.model.home.WeekBestLink import org.sopt.remote.home.api.HomeService @@ -19,4 +20,7 @@ class RemoteHomeDataSourceImpl @Inject constructor( override suspend fun getWeekBestLink(): List = homeService.getWeekBestLink().data!!.map { it.toCoreModel() } + + override suspend fun getRecentSavedLink(): List = + homeService.getRecentSavedLink().data!!.map { it.toCoreModel() } } diff --git a/data-remote/home/src/main/java/org/sopt/remote/home/response/ResponseRecentSavedLinkDto.kt b/data-remote/home/src/main/java/org/sopt/remote/home/response/ResponseRecentSavedLinkDto.kt new file mode 100644 index 00000000..5fbf1df7 --- /dev/null +++ b/data-remote/home/src/main/java/org/sopt/remote/home/response/ResponseRecentSavedLinkDto.kt @@ -0,0 +1,30 @@ +package org.sopt.remote.home.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.sopt.model.home.RecentSavedLink + +@Serializable +data class ResponseRecentSavedLinkDto( + @SerialName("toastId") + val toastId: Long, + @SerialName("toastTitle") + val toastTitle: String, + @SerialName("linkUrl") + val linkUrl: String, + @SerialName("isRead") + val isRead: Boolean, + @SerialName("categoryTitle") + val categoryTitle: String?, + @SerialName("thumbnailUrl") + val thumbnailUrl: String?, +) + +internal fun ResponseRecentSavedLinkDto.toCoreModel() = RecentSavedLink( + toastId = toastId, + toastTitle = toastTitle, + linkUrl = linkUrl, + isRead = isRead, + categoryTitle = categoryTitle, + thumbnailUrl = thumbnailUrl, +) diff --git a/data/home/src/main/java/org/sopt/home/datasource/RemoteHomeDataSource.kt b/data/home/src/main/java/org/sopt/home/datasource/RemoteHomeDataSource.kt index d4c22f58..06b841ef 100644 --- a/data/home/src/main/java/org/sopt/home/datasource/RemoteHomeDataSource.kt +++ b/data/home/src/main/java/org/sopt/home/datasource/RemoteHomeDataSource.kt @@ -1,6 +1,7 @@ package org.sopt.home.datasource import org.sopt.model.home.MainPageData +import org.sopt.model.home.RecentSavedLink import org.sopt.model.home.RecommendLink import org.sopt.model.home.WeekBestLink @@ -8,4 +9,5 @@ interface RemoteHomeDataSource { suspend fun getMainPageUserClip(): MainPageData suspend fun getRecommendSite(): List suspend fun getWeekBestLink(): List + suspend fun getRecentSavedLink(): List } diff --git a/data/home/src/main/java/org/sopt/home/repository/HomeRepoImpl.kt b/data/home/src/main/java/org/sopt/home/repository/HomeRepoImpl.kt index 75a241a7..b8794c3f 100644 --- a/data/home/src/main/java/org/sopt/home/repository/HomeRepoImpl.kt +++ b/data/home/src/main/java/org/sopt/home/repository/HomeRepoImpl.kt @@ -2,6 +2,7 @@ package org.sopt.home.repository import org.sopt.home.datasource.RemoteHomeDataSource import org.sopt.model.home.MainPageData +import org.sopt.model.home.RecentSavedLink import org.sopt.model.home.RecommendLink import org.sopt.model.home.WeekBestLink import javax.inject.Inject @@ -17,4 +18,7 @@ class HomeRepoImpl @Inject constructor( override suspend fun getWeekBestLink(): Result> = runCatching { remoteHomeDataSource.getWeekBestLink() } + + override suspend fun getRecentSavedLink(): Result> = + runCatching { remoteHomeDataSource.getRecentSavedLink() } } diff --git a/domain/home/src/main/java/org/sopt/home/repository/HomeRepository.kt b/domain/home/src/main/java/org/sopt/home/repository/HomeRepository.kt index a60a7f74..d6eda74b 100644 --- a/domain/home/src/main/java/org/sopt/home/repository/HomeRepository.kt +++ b/domain/home/src/main/java/org/sopt/home/repository/HomeRepository.kt @@ -1,6 +1,7 @@ package org.sopt.home.repository import org.sopt.model.home.MainPageData +import org.sopt.model.home.RecentSavedLink import org.sopt.model.home.RecommendLink import org.sopt.model.home.WeekBestLink @@ -8,4 +9,5 @@ interface HomeRepository { suspend fun getMainPageUserClip(): Result suspend fun getRecommendSite(): Result> suspend fun getWeekBestLink(): Result> + suspend fun getRecentSavedLink(): Result> } diff --git a/domain/home/src/main/java/org/sopt/home/usecase/GetRecentSavedLink.kt b/domain/home/src/main/java/org/sopt/home/usecase/GetRecentSavedLink.kt new file mode 100644 index 00000000..e0d33fde --- /dev/null +++ b/domain/home/src/main/java/org/sopt/home/usecase/GetRecentSavedLink.kt @@ -0,0 +1,11 @@ +package org.sopt.home.usecase + +import org.sopt.home.repository.HomeRepository +import org.sopt.model.home.RecentSavedLink +import javax.inject.Inject + +class GetRecentSavedLink @Inject constructor( + private val homeRepository: HomeRepository, +) { + suspend operator fun invoke(): Result> = homeRepository.getRecentSavedLink() +} diff --git a/feature/home/src/main/java/org/sopt/home/HomeContract.kt b/feature/home/src/main/java/org/sopt/home/HomeContract.kt index 493dd45a..e73040e5 100644 --- a/feature/home/src/main/java/org/sopt/home/HomeContract.kt +++ b/feature/home/src/main/java/org/sopt/home/HomeContract.kt @@ -3,6 +3,7 @@ package org.sopt.home import org.sopt.home.model.UpdatePriority import org.sopt.model.category.Category import org.sopt.model.home.PopupInfo +import org.sopt.model.home.RecentSavedLink import org.sopt.model.home.RecommendLink import org.sopt.model.home.WeekBestLink @@ -13,6 +14,7 @@ data class HomeState( val categoryList: List = emptyList(), val weekBestLink: List = emptyList(), val recommendLink: List = emptyList(), + val recentSavedLink: List = emptyList(), val url: String = "", val categoryId: Long? = 0, val categoryName: String? = "전체 클립", @@ -33,8 +35,9 @@ sealed interface HomeSideEffect { data object NavigateSetting : HomeSideEffect data object NavigateSearch : HomeSideEffect data object NavigateClipLink : HomeSideEffect + data object NavigateAllClip : HomeSideEffect data object NavigateWebView : HomeSideEffect - data object ShowBottomSheet : HomeSideEffect data object ShowPopupInfo : HomeSideEffect data object ShowUpdateDialog : HomeSideEffect + data object NavigateSaveLink : HomeSideEffect } diff --git a/feature/home/src/main/java/org/sopt/home/HomeFragment.kt b/feature/home/src/main/java/org/sopt/home/HomeFragment.kt index 25fed57e..83099eb0 100644 --- a/feature/home/src/main/java/org/sopt/home/HomeFragment.kt +++ b/feature/home/src/main/java/org/sopt/home/HomeFragment.kt @@ -5,8 +5,6 @@ import android.view.View import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import dagger.hilt.android.AndroidEntryPoint -import designsystem.components.bottomsheet.LinkMindBottomSheet -import designsystem.components.toast.linkMindSnackBar import org.orbitmvi.orbit.viewmodel.observe import org.sopt.common.util.delSpace import org.sopt.home.adapter.HomeClipAdapter @@ -36,6 +34,7 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. collectState() navigateToSetting() navigateToSearch() + navigateToAllClip() } private fun initView() { @@ -53,7 +52,7 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. binding.tvHomeUserClipName.text = homeState.nickName binding.tvHomeToastLinkCount.text = "${homeState.readToastNum}개의 링크" binding.pbLinkmindHome.setProgressBarMain(homeState.calculateProgress()) - homeClipAdapter.submitList(homeState.categoryList) + homeClipAdapter.submitList(homeState.recentSavedLink) homeWeekLinkAdapter.submitList(homeState.weekBestLink) homeWeekRecommendLinkAdapter.submitList(homeState.recommendLink) } @@ -65,15 +64,14 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. is HomeSideEffect.NavigateClipLink -> navigateToDestination( "featureSaveLink://ClipLinkFragment/${viewModel.container.stateFlow.value.categoryId}/${viewModel.container.stateFlow.value.categoryName}", ) - - is HomeSideEffect.ShowBottomSheet -> showHomeBottomSheet() + is HomeSideEffect.NavigateSaveLink -> navigateToDestinationWithoutAnim("featureSaveLink://saveLinkFragment?clipboardLink=") is HomeSideEffect.NavigateWebView -> { val encodedURL = URLEncoder.encode(viewModel.container.stateFlow.value.url, StandardCharsets.UTF_8.toString()) navigateToDestination( "featureSaveLink://webViewFragment/${0}/${false}/${false}/$encodedURL", ) } - + is HomeSideEffect.NavigateAllClip -> navigateToDestinationWithoutAnim("featureSaveLink://ClipLinkFragment/0/전체 클립") is HomeSideEffect.ShowPopupInfo -> showPopupInfo(viewModel.container.stateFlow.value.popupList) is HomeSideEffect.ShowUpdateDialog -> showUpdateDialog(viewModel.container.stateFlow.value.marketUpdate) } @@ -86,6 +84,7 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. viewModel.apply { getMainPageUserClip() getRecommendSite() + getRecentSavedClip() getWeekBestLink() getPopupListInfo() checkMarketUpdateState() @@ -104,18 +103,22 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. } } + private fun navigateToAllClip() { + binding.ivRecentClip.onThrottleClick { + viewModel.navigateAllClip() + } + } + private fun setClipAdapter() { homeClipAdapter = HomeClipAdapter( onClickClip = { - viewModel.navigateClipLink(it.categoryId, it.categoryTitle) + viewModel.navigateWebview(it.linkUrl) }, onClickEmptyClip = { - viewModel.showBottomSheet() + viewModel.navigateSaveLink() }, ) binding.rvHomeClip.adapter = homeClipAdapter - val spacingClipInPixels = resources.getDimensionPixelSize(R.dimen.spacing_11) - binding.rvHomeClip.addItemDecoration(ItemDecoration(2, spacingClipInPixels)) } private fun setWeekLinkAdapter() { @@ -149,20 +152,11 @@ class HomeFragment : BindingFragment({ FragmentHomeBinding. findNavController().navigate(request, navOptions) } - private fun showHomeBottomSheet() { - val linkMindBottomSheet = LinkMindBottomSheet(requireContext()) - linkMindBottomSheet.show() - linkMindBottomSheet.apply { - setBottomSheetHint(org.sopt.mainfeature.R.string.home_new_clip_info) - setTitle(org.sopt.mainfeature.R.string.home_add_clip) - setErroMsg(org.sopt.mainfeature.R.string.home_error_clip_info) - bottomSheetConfirmBtnClick { - if (showErrorMsg()) return@bottomSheetConfirmBtnClick - viewModel.saveCategoryTitle(it) - dismiss() - requireContext().linkMindSnackBar(binding.vSnack, "클립 생성 완료!", false) - } - } + private fun navigateToDestinationWithoutAnim(destination: String) { + val (request, navOptions) = DeepLinkUtil.getNavRequestNotPopUpAndOption( + destination.delSpace(), + ) + findNavController().navigate(request, navOptions) } private fun showPopupInfo(popupList: List) { diff --git a/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt b/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt index 57035cfb..60b9d2f7 100644 --- a/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt +++ b/feature/home/src/main/java/org/sopt/home/HomeViewModel.kt @@ -23,10 +23,10 @@ import org.sopt.domain.category.category.usecase.PostAddCategoryTitleUseCase import org.sopt.home.model.UpdatePriority import org.sopt.home.usecase.GetMainPageUserClip import org.sopt.home.usecase.GetPopupInfo +import org.sopt.home.usecase.GetRecentSavedLink import org.sopt.home.usecase.GetRecommendSite import org.sopt.home.usecase.GetWeekBestLink import org.sopt.home.usecase.PatchPopupInvisible -import org.sopt.model.category.Category import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale @@ -38,6 +38,7 @@ class HomeViewModel @Inject constructor( private val getMainPageUserClip: GetMainPageUserClip, private val getRecommendSite: GetRecommendSite, private val getWeekBestLink: GetWeekBestLink, + private val getRecentSavedLink: GetRecentSavedLink, private val postAddCategoryTitle: PostAddCategoryTitleUseCase, private val patchPopupInvisible: PatchPopupInvisible, private val getPopupInfo: GetPopupInfo, @@ -46,35 +47,13 @@ class HomeViewModel @Inject constructor( override val container: Container = container(HomeState()) - fun saveCategoryTitle(categoryTitle: String) = viewModelScope.launch { - postAddCategoryTitle( - PostAddCategoryTitleUseCase.Param( - categoryTitle = categoryTitle, - ), - ).onSuccess { - getMainPageUserClip() - }.onFailure { Log.d("saveCategoryTitleFail", "$it") } - } - fun getMainPageUserClip() = intent { getMainPageUserClip.invoke().onSuccess { reduce { - val tempCategoryList = listOf( - Category( - 0, - "전체 카테고리", - it.allToastNum.toLong(), - ), - ) + it.mainCategoryDto - val categoryList = if (tempCategoryList.size < 4) tempCategoryList + null else tempCategoryList - val finalCategoryList = categoryList.distinctBy { it?.categoryId } state.copy( nickName = it.nickName, allToastNum = it.allToastNum, readToastNum = it.readToastNum, - categoryList = ( - finalCategoryList - ), ) } }.onFailure { @@ -82,6 +61,22 @@ class HomeViewModel @Inject constructor( } } + fun getRecentSavedClip() = intent { + getRecentSavedLink.invoke().onSuccess { + if (it.isEmpty()) { + reduce { + state.copy(recentSavedLink = listOf(null)) + } + } else { + reduce { + state.copy(recentSavedLink = (container.stateFlow.value.recentSavedLink + it).distinctBy { it?.toastId }) + } + } + }.onFailure { + Log.d("RecentSaved", "$it") + } + } + fun getRecommendSite() = intent { getRecommendSite.invoke().onSuccess { reduce { @@ -151,18 +146,8 @@ class HomeViewModel @Inject constructor( fun navigateSearch() = intent { postSideEffect(HomeSideEffect.NavigateSearch) } fun navigateSetting() = intent { postSideEffect(HomeSideEffect.NavigateSetting) } - fun showBottomSheet() = intent { postSideEffect(HomeSideEffect.ShowBottomSheet) } - - @OptIn(OrbitExperimental::class) - fun navigateClipLink(categoryId: Long?, categoryName: String?) = blockingIntent { - reduce { - state.copy( - categoryId = categoryId, - categoryName = categoryName, - ) - } - postSideEffect(HomeSideEffect.NavigateClipLink) - } + fun navigateSaveLink() = intent { postSideEffect(HomeSideEffect.NavigateSaveLink) } + fun navigateAllClip() = intent { postSideEffect(HomeSideEffect.NavigateAllClip) } @OptIn(OrbitExperimental::class) fun navigateWebview(url: String) = blockingIntent { diff --git a/feature/home/src/main/java/org/sopt/home/adapter/HomeClipAdapter.kt b/feature/home/src/main/java/org/sopt/home/adapter/HomeClipAdapter.kt index 84b6fab9..f5e76860 100644 --- a/feature/home/src/main/java/org/sopt/home/adapter/HomeClipAdapter.kt +++ b/feature/home/src/main/java/org/sopt/home/adapter/HomeClipAdapter.kt @@ -5,13 +5,13 @@ import android.view.ViewGroup import androidx.recyclerview.widget.ListAdapter import org.sopt.home.databinding.ItemHomeClipBinding import org.sopt.home.viewholder.HomeClipViewHolder -import org.sopt.model.category.Category +import org.sopt.model.home.RecentSavedLink import org.sopt.ui.view.ItemDiffCallback class HomeClipAdapter( - private val onClickClip: (Category) -> Unit, + private val onClickClip: (RecentSavedLink) -> Unit, private val onClickEmptyClip: () -> Unit, -) : ListAdapter(DiffUtil) { +) : ListAdapter(DiffUtil) { override fun onBindViewHolder(holder: HomeClipViewHolder, position: Int) { holder.onBind(getItem(position), position) } @@ -24,11 +24,11 @@ class HomeClipAdapter( ) } - override fun getItemCount() = currentList.size.coerceAtMost(4) + override fun getItemCount() = currentList.size.coerceAtMost(3) companion object { - private val DiffUtil = ItemDiffCallback( - onItemsTheSame = { old, new -> old.categoryId == new.categoryId }, + private val DiffUtil = ItemDiffCallback( + onItemsTheSame = { old, new -> old.toastId == new.toastId }, onContentsTheSame = { old, new -> old == new }, ) } diff --git a/feature/home/src/main/java/org/sopt/home/viewholder/HomeClipViewHolder.kt b/feature/home/src/main/java/org/sopt/home/viewholder/HomeClipViewHolder.kt index 8edd32c7..93395f85 100644 --- a/feature/home/src/main/java/org/sopt/home/viewholder/HomeClipViewHolder.kt +++ b/feature/home/src/main/java/org/sopt/home/viewholder/HomeClipViewHolder.kt @@ -1,24 +1,24 @@ package org.sopt.home.viewholder +import android.view.View import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView +import coil.load import org.sopt.home.databinding.ItemHomeClipBinding -import org.sopt.model.category.Category +import org.sopt.model.home.RecentSavedLink +import org.sopt.ui.view.onThrottleClick class HomeClipViewHolder( private val binding: ItemHomeClipBinding, - private val onClickClip: (Category) -> Unit, + private val onClickClip: (RecentSavedLink) -> Unit, private val onClickEmptyClip: () -> Unit, ) : RecyclerView.ViewHolder(binding.root) { - fun onBind(data: Category?, position: Int) { - if (position == 0) { - binding.ivHomeClip.setImageResource(org.sopt.mainfeature.R.drawable.ic_clip_all_24) - } + fun onBind(data: RecentSavedLink?, position: Int) { if (data == null) { with(binding) { - clItemClip.isGone = true - clItemClipEmpty.isVisible = true + clHomeItemClip.isGone = true + clHomeItemClipEmpty.isVisible = true root.setOnClickListener { onClickEmptyClip() } @@ -26,11 +26,19 @@ class HomeClipViewHolder( return } with(binding) { - tvItemClipTitle.text = data.categoryTitle - tvItemClipCount.text = data.toastNum.toString() + "개" - root.setOnClickListener { - onClickClip(data) + tvLinkTitle.text = data.toastTitle + tvLinkUrl.text = data.linkUrl + binding.tvLinkTitle.setVisible(!data.categoryTitle.isNullOrEmpty()) + tvLinkClipTitle.text = data.categoryTitle + ivLinkThumnail.load(data.thumbnailUrl) + tvItemClipLink.setVisible(data.isRead) + root.onThrottleClick { + onClickClip.invoke(data) } } } + + private fun View.setVisible(value: Boolean) { + visibility = if (value) View.VISIBLE else View.GONE + } } diff --git a/feature/home/src/main/res/layout/fragment_home.xml b/feature/home/src/main/res/layout/fragment_home.xml index c9c935c1..3958f0ef 100644 --- a/feature/home/src/main/res/layout/fragment_home.xml +++ b/feature/home/src/main/res/layout/fragment_home.xml @@ -189,18 +189,27 @@ app:layout_constraintStart_toEndOf="@+id/tv_home_user_clip_name" app:layout_constraintTop_toTopOf="@id/tv_home_user_clip_name" /> + + + android:layout_marginBottom="8dp" + android:background="@drawable/shape_white_fill_12_rect"> + + + android:id="@+id/iv_link_thumnail" + android:layout_width="74dp" + android:layout_height="74dp" + android:adjustViewBounds="true" + android:elevation="0dp" + android:scaleType="centerCrop" + android:src="@drawable/img_link_thumb" + app:layout_constraintDimensionRatio="1:1" /> + - + + + + + app:layout_constraintTop_toTopOf="parent" + tools:text="세부 클립명" /> + + + + + + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent"> +