crear reportes

This commit is contained in:
erik-everardo 2024-04-20 11:45:12 -06:00
parent ce8ec23100
commit bf886c845d
14 changed files with 235 additions and 19 deletions

View File

@ -36,6 +36,7 @@ import com.isolaatti.images.image_chooser.ui.ImageChooserContract
import com.isolaatti.posting.link_creator.presentation.LinkCreatorViewModel
import com.isolaatti.posting.link_creator.ui.LinkCreatorFragment
import com.isolaatti.profile.ui.ProfileActivity
import com.isolaatti.reports.data.ContentType
import com.isolaatti.reports.ui.NewReportBottomSheetDialogFragment
import dagger.hilt.android.AndroidEntryPoint
import io.noties.markwon.AbstractMarkwonPlugin
@ -75,7 +76,8 @@ class BottomSheetPostComments() : BottomSheetDialogFragment(), OnUserInteractedC
}
Options.Option.OPTION_REPORT -> {
optionsViewModel.handle()
NewReportBottomSheetDialogFragment.newInstance().show(childFragmentManager, NewReportBottomSheetDialogFragment.LOG_TAG)
NewReportBottomSheetDialogFragment.newInstance(ContentType.Comment, comment.id.toString())
.show(childFragmentManager, NewReportBottomSheetDialogFragment.LOG_TAG)
}
}
}

View File

@ -33,6 +33,8 @@ import com.isolaatti.posting.posts.presentation.PostListingViewModel
import com.isolaatti.posting.posts.presentation.PostsRecyclerViewAdapter
import com.isolaatti.posting.posts.viewer.ui.PostViewerActivity
import com.isolaatti.profile.ui.ProfileActivity
import com.isolaatti.reports.data.ContentType
import com.isolaatti.reports.ui.NewReportBottomSheetDialogFragment
import dagger.hilt.android.AndroidEntryPoint
import io.noties.markwon.AbstractMarkwonPlugin
import io.noties.markwon.Markwon
@ -209,6 +211,8 @@ class PostListingFragment : Fragment(), OnUserInteractedWithPostCallback {
}
Options.Option.OPTION_REPORT -> {
optionsViewModel.handle()
NewReportBottomSheetDialogFragment.newInstance(ContentType.Post, post.id.toString())
.show(childFragmentManager, NewReportBottomSheetDialogFragment.LOG_TAG)
}
}
}

View File

@ -49,6 +49,7 @@ import com.isolaatti.posting.posts.viewer.ui.PostViewerActivity
import com.isolaatti.profile.domain.entity.UserProfile
import com.isolaatti.profile.presentation.EditProfileContract
import com.isolaatti.profile.presentation.ProfileViewModel
import com.isolaatti.reports.data.ContentType
import com.isolaatti.reports.ui.NewReportBottomSheetDialogFragment
import com.isolaatti.utils.UrlGen
import dagger.hilt.android.AndroidEntryPoint
@ -266,7 +267,7 @@ class ProfileMainFragment : Fragment() {
}
Options.Option.OPTION_REPORT -> {
optionsViewModel.handle()
NewReportBottomSheetDialogFragment.newInstance().show(childFragmentManager, NewReportBottomSheetDialogFragment.LOG_TAG)
NewReportBottomSheetDialogFragment.newInstance(ContentType.Post, post.id.toString()).show(childFragmentManager, NewReportBottomSheetDialogFragment.LOG_TAG)
}
}
}
@ -329,6 +330,7 @@ class ProfileMainFragment : Fragment() {
true
}
R.id.report_profile_menu_item -> {
NewReportBottomSheetDialogFragment.newInstance(ContentType.Profile, viewModel.profileId.toString()).show(childFragmentManager, NewReportBottomSheetDialogFragment.LOG_TAG)
true
}
R.id.block_profile_menu_item -> {

View File

@ -0,0 +1,24 @@
package com.isolaatti.reports
import com.isolaatti.connectivity.RetrofitClient
import com.isolaatti.reports.data.ReportsApi
import com.isolaatti.reports.data.ReportsRepositoryImpl
import com.isolaatti.reports.domain.ReportsRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
class Module {
@Provides
fun provideReportsApi(retrofitClient: RetrofitClient): ReportsApi {
return retrofitClient.client.create(ReportsApi::class.java)
}
@Provides
fun provideReportsRepository(reportsApi: ReportsApi): ReportsRepository {
return ReportsRepositoryImpl(reportsApi)
}
}

View File

@ -0,0 +1,8 @@
package com.isolaatti.reports.data
data class CreateReportDto(
val reason: Int,
val comment: String,
val contentType: Int,
val contentId: String
)

View File

@ -0,0 +1,9 @@
package com.isolaatti.reports.data
data class ReportDto(
val id: String,
val reportReason: Int,
val userId: Int,
val userComment: String,
val status: Int
)

View File

@ -0,0 +1,12 @@
package com.isolaatti.reports.data
// keep enum constants in this specific order
enum class ContentType {
Post, Comment, Profile, Squad, Picture, Audio
}
enum class ReportStatus {
Open, Resolved, InReview
}

View File

@ -0,0 +1,16 @@
package com.isolaatti.reports.data
import com.isolaatti.common.ResultDto
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
interface ReportsApi {
@POST("reports")
fun createReport(@Body createReportDto: CreateReportDto): Call<ReportDto>
@GET("reports/by_me")
fun gerReportsByMe(): Call<ResultDto<ReportDto>>
}

View File

@ -0,0 +1,33 @@
package com.isolaatti.reports.data
import android.util.Log
import com.isolaatti.reports.domain.ReportsRepository
import com.isolaatti.utils.Resource
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import retrofit2.awaitResponse
import javax.inject.Inject
class ReportsRepositoryImpl @Inject constructor(private val reportsApi: ReportsApi) : ReportsRepository {
companion object {
const val LOG_TAG = "ReportsRepositoryImpl"
}
override fun createReport(createReportDto: CreateReportDto): Flow<Resource<ReportDto>> = flow {
try {
emit(Resource.Loading())
val response = reportsApi.createReport(createReportDto).awaitResponse()
if(response.isSuccessful) {
emit(Resource.Success(response.body()))
} else {
emit(Resource.Error(Resource.Error.mapErrorCode(response.code())))
}
} catch(e: Exception) {
Log.e(LOG_TAG, "error creating report: ${e.message}")
emit(Resource.Error(Resource.Error.ErrorType.OtherError))
}
}
override fun getReportsByMe(): Flow<Resource<List<ReportDto>>> {
TODO("Not yet implemented")
}
}

View File

@ -0,0 +1,12 @@
package com.isolaatti.reports.domain
import com.isolaatti.reports.data.CreateReportDto
import com.isolaatti.reports.data.ReportDto
import com.isolaatti.utils.Resource
import kotlinx.coroutines.flow.Flow
interface ReportsRepository {
fun createReport(createReportDto: CreateReportDto): Flow<Resource<ReportDto>>
fun getReportsByMe(): Flow<Resource<List<ReportDto>>>
}

View File

@ -0,0 +1,30 @@
package com.isolaatti.reports.presentation
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.isolaatti.reports.data.ContentType
import com.isolaatti.reports.data.CreateReportDto
import com.isolaatti.reports.data.ReportDto
import com.isolaatti.reports.domain.ReportsRepository
import com.isolaatti.utils.Resource
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class CreateReportViewModel @Inject constructor(private val reportsRepository: ReportsRepository) : ViewModel() {
val createReportResponse: MutableLiveData<Resource<ReportDto>> = MutableLiveData()
fun createReport(reason: Int, comment: String, contentType: ContentType, contentId: String) {
viewModelScope.launch {
reportsRepository.createReport(CreateReportDto(reason, comment, contentType.ordinal, contentId)).onEach {
createReportResponse.postValue(it)
}.flowOn(Dispatchers.IO).launchIn(this)
}
}
}

View File

@ -4,12 +4,33 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.viewModels
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.isolaatti.R
import com.isolaatti.databinding.BottomSheetNewReportBinding
import com.isolaatti.reports.data.ContentType
import com.isolaatti.reports.presentation.CreateReportViewModel
import com.isolaatti.utils.Resource
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class NewReportBottomSheetDialogFragment : BottomSheetDialogFragment() {
private lateinit var binding: BottomSheetNewReportBinding
private val viewModel: CreateReportViewModel by viewModels()
private lateinit var contentType: ContentType
private lateinit var contentId: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
contentType = arguments?.getSerializable(ARG_CONTENT_TYPE) as ContentType
contentId = arguments?.getString(ARG_CONTENT_ID)!!
}
override fun onCreateView(
inflater: LayoutInflater,
@ -21,10 +42,45 @@ class NewReportBottomSheetDialogFragment : BottomSheetDialogFragment() {
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
ArrayAdapter.createFromResource(requireContext(), R.array.report_reasons, android.R.layout.simple_spinner_item).also { spinnerAdapter ->
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
binding.reportReasons.adapter = spinnerAdapter
}
binding.reportUserComment.editText?.doOnTextChanged { text, start, before, count ->
binding.submitReportButton.isEnabled = count > 0
}
binding.submitReportButton.setOnClickListener {
viewModel.createReport(binding.reportReasons.selectedItemPosition, binding.reportUserComment.editText?.text.toString(), contentType, contentId)
}
viewModel.createReportResponse.observe(viewLifecycleOwner) { resource ->
when(resource) {
is Resource.Error -> {}
is Resource.Loading -> {}
is Resource.Success -> {
Toast.makeText(requireContext(), R.string.report_submitted_successfully, Toast.LENGTH_SHORT).show()
dialog?.dismiss()
}
}
}
}
companion object {
const val LOG_TAG = "NewReportBottomSheetDialogFragment"
fun newInstance(): NewReportBottomSheetDialogFragment {
return NewReportBottomSheetDialogFragment()
const val ARG_CONTENT_TYPE = "contentType"
const val ARG_CONTENT_ID = "contentId"
fun newInstance(contentType: ContentType, contentId: String): NewReportBottomSheetDialogFragment {
return NewReportBottomSheetDialogFragment().apply {
arguments = Bundle().apply {
putSerializable(ARG_CONTENT_TYPE, contentType)
putString(ARG_CONTENT_ID, contentId)
}
}
}
}
}

View File

@ -22,27 +22,21 @@
app:layout_constraintTop_toBottomOf="@id/drag_handle"/>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/menu"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
<Spinner
android:id="@+id/report_reasons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/title"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp">
<AutoCompleteTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none" />
</com.google.android.material.textfield.TextInputLayout>
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp"
android:layout_marginHorizontal="16dp"/>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textField"
android:id="@+id/report_user_comment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/menu"
app:layout_constraintTop_toBottomOf="@id/report_reasons"
android:layout_marginTop="16dp"
android:layout_marginHorizontal="16dp">
@ -55,15 +49,17 @@
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/submit_report_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Submit report"
app:layout_constraintTop_toBottomOf="@id/textField"
app:layout_constraintTop_toBottomOf="@id/report_user_comment"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginTop="16dp"
android:layout_marginHorizontal="16dp"
app:layout_goneMarginTop="16dp"
android:enabled="false"
style="?attr/materialIconButtonFilledTonalStyle"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -205,4 +205,16 @@
<string name="browse_profiles">Browse profiles</string>
<string name="app_version">v%s</string>
<string name="new_report">New report</string>
<string name="report_submitted_successfully">Report submitted successfully</string>
<string-array name="report_reasons">
<item>Spam</item>
<item>Explicit content</item>
<item>Children at risk</item>
<item>Piracy</item>
<item>Copyright protected content</item>
<item>Hate and/or discrimination</item>
<item>Identity fraud</item>
<item>Fraudulent IA generated content</item>
<item>People in danger</item>
</string-array>
</resources>