diff --git a/core/design/src/main/res/drawable/icn_home_acitve_24.xml b/core/design/src/main/res/drawable/icn_home_active_24.xml similarity index 100% rename from core/design/src/main/res/drawable/icn_home_acitve_24.xml rename to core/design/src/main/res/drawable/icn_home_active_24.xml diff --git a/data/src/main/java/com/tripbook/tripbook/data/repository/ArticleRepositoryImpl.kt b/data/src/main/java/com/tripbook/tripbook/data/repository/ArticleRepositoryImpl.kt index 8988d47..8530ebd 100644 --- a/data/src/main/java/com/tripbook/tripbook/data/repository/ArticleRepositoryImpl.kt +++ b/data/src/main/java/com/tripbook/tripbook/data/repository/ArticleRepositoryImpl.kt @@ -6,13 +6,14 @@ import androidx.paging.PagingConfig import androidx.paging.PagingData import com.tripbook.libs.network.NetworkResult import com.tripbook.libs.network.model.request.ArticleRequest +import com.tripbook.libs.network.model.request.ReportRequest import com.tripbook.libs.network.safeApiCall import com.tripbook.libs.network.service.TripArticlesService import com.tripbook.tripbook.data.datasource.ArticlePagingSource import com.tripbook.tripbook.data.mapper.toLocationResponse import com.tripbook.tripbook.domain.model.ArticleDetail -import com.tripbook.tripbook.domain.model.Location import com.tripbook.tripbook.domain.model.LikeArticle +import com.tripbook.tripbook.domain.model.Location import com.tripbook.tripbook.domain.model.SortType import com.tripbook.tripbook.domain.repository.ArticleRepository import kotlinx.coroutines.Dispatchers @@ -73,6 +74,25 @@ class ArticleRepositoryImpl @Inject constructor( } } + override fun reportArticle(articleId: Long, content: String): Flow = safeApiCall(Dispatchers.IO) { + val reportRequest = ReportRequest( + articleId, + content + ) + tripArticlesService.reportArticle(reportRequest) + }.map { response -> + when (response) { + is NetworkResult.Success -> { + response.value + true + } + + else -> { + false + } + } + } + override fun saveTempArticle( tempId: Long?, title: String, @@ -81,7 +101,7 @@ class ArticleRepositoryImpl @Inject constructor( imageList: List?, locationList: List? ): Flow = safeApiCall(Dispatchers.IO) { - val articleResponse = ArticleRequest( + val articleRequest = ArticleRequest( tempId, title, content, @@ -91,7 +111,7 @@ class ArticleRepositoryImpl @Inject constructor( ) tripArticlesService.tempSaveArticle( - articleResponse + articleRequest ) }.map { when (it) { diff --git a/domain/src/main/java/com/tripbook/tripbook/domain/repository/ArticleRepository.kt b/domain/src/main/java/com/tripbook/tripbook/domain/repository/ArticleRepository.kt index 1e384a6..2338cfd 100644 --- a/domain/src/main/java/com/tripbook/tripbook/domain/repository/ArticleRepository.kt +++ b/domain/src/main/java/com/tripbook/tripbook/domain/repository/ArticleRepository.kt @@ -2,8 +2,8 @@ package com.tripbook.tripbook.domain.repository import androidx.paging.PagingData import com.tripbook.tripbook.domain.model.ArticleDetail -import com.tripbook.tripbook.domain.model.Location import com.tripbook.tripbook.domain.model.LikeArticle +import com.tripbook.tripbook.domain.model.Location import com.tripbook.tripbook.domain.model.SortType import kotlinx.coroutines.flow.Flow @@ -16,6 +16,8 @@ interface ArticleRepository { fun getArticles(word: String, sortType: SortType): Flow> fun deleteArticle(articleId: Long): Flow + fun reportArticle(articleId: Long, content:String): Flow + fun saveTempArticle( tempId: Long?, title: String, diff --git a/domain/src/main/java/com/tripbook/tripbook/domain/usecase/ArticleReportUseCase.kt b/domain/src/main/java/com/tripbook/tripbook/domain/usecase/ArticleReportUseCase.kt new file mode 100644 index 0000000..5208935 --- /dev/null +++ b/domain/src/main/java/com/tripbook/tripbook/domain/usecase/ArticleReportUseCase.kt @@ -0,0 +1,12 @@ +package com.tripbook.tripbook.domain.usecase + +import com.tripbook.tripbook.domain.repository.ArticleRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class ArticleReportUseCase @Inject constructor( + private val repository: ArticleRepository +) { + operator fun invoke(articleId: Long, content: String): Flow = repository.reportArticle(articleId, content) + +} \ No newline at end of file diff --git a/libs/network/src/main/java/com/tripbook/libs/network/model/request/ReportRequest.kt b/libs/network/src/main/java/com/tripbook/libs/network/model/request/ReportRequest.kt new file mode 100644 index 0000000..f42e6cf --- /dev/null +++ b/libs/network/src/main/java/com/tripbook/libs/network/model/request/ReportRequest.kt @@ -0,0 +1,6 @@ +package com.tripbook.libs.network.model.request + +data class ReportRequest ( + val articleId: Long, + val content: String +) \ No newline at end of file diff --git a/libs/network/src/main/java/com/tripbook/libs/network/service/TripArticlesService.kt b/libs/network/src/main/java/com/tripbook/libs/network/service/TripArticlesService.kt index 9cb482a..7b02a39 100644 --- a/libs/network/src/main/java/com/tripbook/libs/network/service/TripArticlesService.kt +++ b/libs/network/src/main/java/com/tripbook/libs/network/service/TripArticlesService.kt @@ -1,6 +1,7 @@ package com.tripbook.libs.network.service import com.tripbook.libs.network.model.request.ArticleRequest +import com.tripbook.libs.network.model.request.ReportRequest import com.tripbook.libs.network.model.response.ArticleDetailResponse import com.tripbook.libs.network.model.response.ArticleResponse import com.tripbook.libs.network.model.response.LikeArticleResponse @@ -38,10 +39,14 @@ interface TripArticlesService { @Path("articleId") articledId: Long ) : UnitResponse + @POST("articles/report") + suspend fun reportArticle( + @Body reportRequest: ReportRequest + ) : UnitResponse @POST("articles/temp") suspend fun tempSaveArticle( - @Body articleResponse: ArticleRequest + @Body articleRequest: ArticleRequest ): TempArticleResponse @POST("articles") diff --git a/presentation/src/main/java/com/tripbook/tripbook/viewmodel/ArticleViewModel.kt b/presentation/src/main/java/com/tripbook/tripbook/viewmodel/ArticleViewModel.kt index f7cb6b7..5065cd9 100644 --- a/presentation/src/main/java/com/tripbook/tripbook/viewmodel/ArticleViewModel.kt +++ b/presentation/src/main/java/com/tripbook/tripbook/viewmodel/ArticleViewModel.kt @@ -4,6 +4,7 @@ package com.tripbook.tripbook.viewmodel import androidx.lifecycle.viewModelScope import com.tripbook.base.BaseViewModel import com.tripbook.tripbook.domain.usecase.ArticleLikeUseCase +import com.tripbook.tripbook.domain.usecase.ArticleReportUseCase import com.tripbook.tripbook.domain.usecase.MemberUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -17,7 +18,8 @@ import javax.inject.Inject @HiltViewModel class ArticleViewModel @Inject constructor( private val articleLikeUseCase : ArticleLikeUseCase, - private val memberUseCase: MemberUseCase + private val memberUseCase: MemberUseCase, + private val articleReportUseCase: ArticleReportUseCase ) : BaseViewModel() { //좋아요 수 @@ -33,6 +35,18 @@ class ArticleViewModel @Inject constructor( private var _author = MutableStateFlow(false) val author: StateFlow get() = _author + private var _articleId = MutableStateFlow(0) + + private var _reportComplete = MutableStateFlow(false) + val reportComplete: StateFlow get() = _reportComplete + + fun setReportResult(result: Boolean){ + _reportComplete.value = result + } + fun setArticleId(articleId: Long){ + _articleId.value = articleId + } + fun reportArticle(content: String) = articleReportUseCase(_articleId.value, content) fun likeArticle(articleId : Long) = articleLikeUseCase(articleId).onEach { if (it != null) { diff --git a/presentation/src/main/java/com/tripbook/tripbook/views/trip/detail/ReportFragmentDialog.kt b/presentation/src/main/java/com/tripbook/tripbook/views/trip/detail/ReportFragmentDialog.kt new file mode 100644 index 0000000..6d1e126 --- /dev/null +++ b/presentation/src/main/java/com/tripbook/tripbook/views/trip/detail/ReportFragmentDialog.kt @@ -0,0 +1,40 @@ +package com.tripbook.tripbook.views.trip.detail + +import android.widget.Toast +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.lifecycleScope +import com.tripbook.base.BaseDialogFragment +import com.tripbook.tripbook.R +import com.tripbook.tripbook.databinding.FragmentReportDialogBinding +import com.tripbook.tripbook.viewmodel.ArticleViewModel +import kotlinx.coroutines.launch + +class ReportFragmentDialog : BaseDialogFragment(R.layout.fragment_report_dialog) { + + override val viewModel: ArticleViewModel by activityViewModels() + + override fun init() { + binding.viewModel = viewModel + + binding.dialogClose.setOnClickListener { + dismiss() + viewModel.setReportResult(false) + } + binding.btnCancel.setOnClickListener { + dismiss() + viewModel.setReportResult(false) + } + binding.btnReport.setOnClickListener { + if (binding.reportContent.text.isEmpty()) { + Toast.makeText(requireContext(), "게시글 신고사유를 입력해주세요.", Toast.LENGTH_SHORT).show() + } else { + viewLifecycleOwner.lifecycleScope.launch { + viewModel.reportArticle(binding.reportContent.text.toString()).collect{ + viewModel.setReportResult(it) + if(!it) Toast.makeText(requireContext(), "이미 신고된 게시글이거나 에러가 발생했습니다.", Toast.LENGTH_SHORT).show() + } + } + } + } + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/tripbook/tripbook/views/trip/detail/TripDetailFragment.kt b/presentation/src/main/java/com/tripbook/tripbook/views/trip/detail/TripDetailFragment.kt index de62799..f29b34e 100644 --- a/presentation/src/main/java/com/tripbook/tripbook/views/trip/detail/TripDetailFragment.kt +++ b/presentation/src/main/java/com/tripbook/tripbook/views/trip/detail/TripDetailFragment.kt @@ -17,6 +17,7 @@ import com.tripbook.tripbook.viewmodel.TripDetailViewModel import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import javax.inject.Inject +import kotlin.math.abs @AndroidEntryPoint class TripDetailFragment : BaseFragment(R.layout.fragment_trip_detail) { @@ -42,6 +43,7 @@ class TripDetailFragment : BaseFragment= appBarLayout.totalScrollRange) { + } else if (abs(verticalOffset) >= appBarLayout.totalScrollRange) { toolBar.visibility = View.VISIBLE icnBefore.setColorFilter( ContextCompat.getColor( diff --git a/presentation/src/main/res/drawable/bg_edittext.xml b/presentation/src/main/res/drawable/bg_edittext.xml new file mode 100644 index 0000000..ae5c6f8 --- /dev/null +++ b/presentation/src/main/res/drawable/bg_edittext.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/drawable/button_report.xml b/presentation/src/main/res/drawable/button_report.xml new file mode 100644 index 0000000..461b489 --- /dev/null +++ b/presentation/src/main/res/drawable/button_report.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/fragment_report_dialog.xml b/presentation/src/main/res/layout/fragment_report_dialog.xml new file mode 100644 index 0000000..0390c76 --- /dev/null +++ b/presentation/src/main/res/layout/fragment_report_dialog.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/presentation/src/main/res/layout/fragment_trip_detail.xml b/presentation/src/main/res/layout/fragment_trip_detail.xml index e707701..cec5c20 100644 --- a/presentation/src/main/res/layout/fragment_trip_detail.xml +++ b/presentation/src/main/res/layout/fragment_trip_detail.xml @@ -78,7 +78,6 @@ app:layout_constraintStart_toEndOf="@id/icn_before" app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="parent" - android:visibility="gone" app:tint="@color/white" /> 최신순 오래된순 인기순 + 게시글 신고사유를 입력해주세요. + 게시글 신고사유를 입력하세요. + 신고 취소 + 게시글 신고 + 게시글 신고 접수가\n정상적으로 처리되었습니다. + 소중한 의견 감사드립니다. \ No newline at end of file