diff --git a/app/src/main/java/com/isolaatti/posting/comments/ui/BottomSheetPostComments.kt b/app/src/main/java/com/isolaatti/posting/comments/ui/BottomSheetPostComments.kt index e15768a..df71073 100644 --- a/app/src/main/java/com/isolaatti/posting/comments/ui/BottomSheetPostComments.kt +++ b/app/src/main/java/com/isolaatti/posting/comments/ui/BottomSheetPostComments.kt @@ -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) } } } diff --git a/app/src/main/java/com/isolaatti/posting/posts/ui/PostListingFragment.kt b/app/src/main/java/com/isolaatti/posting/posts/ui/PostListingFragment.kt index 805dd67..0175706 100644 --- a/app/src/main/java/com/isolaatti/posting/posts/ui/PostListingFragment.kt +++ b/app/src/main/java/com/isolaatti/posting/posts/ui/PostListingFragment.kt @@ -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) } } } diff --git a/app/src/main/java/com/isolaatti/profile/ui/ProfileMainFragment.kt b/app/src/main/java/com/isolaatti/profile/ui/ProfileMainFragment.kt index 8c4fc75..de92ce3 100644 --- a/app/src/main/java/com/isolaatti/profile/ui/ProfileMainFragment.kt +++ b/app/src/main/java/com/isolaatti/profile/ui/ProfileMainFragment.kt @@ -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 -> { diff --git a/app/src/main/java/com/isolaatti/reports/Module.kt b/app/src/main/java/com/isolaatti/reports/Module.kt new file mode 100644 index 0000000..5768147 --- /dev/null +++ b/app/src/main/java/com/isolaatti/reports/Module.kt @@ -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) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/reports/data/CreateReportDto.kt b/app/src/main/java/com/isolaatti/reports/data/CreateReportDto.kt new file mode 100644 index 0000000..d4453d2 --- /dev/null +++ b/app/src/main/java/com/isolaatti/reports/data/CreateReportDto.kt @@ -0,0 +1,8 @@ +package com.isolaatti.reports.data + +data class CreateReportDto( + val reason: Int, + val comment: String, + val contentType: Int, + val contentId: String +) \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/reports/data/ReportDto.kt b/app/src/main/java/com/isolaatti/reports/data/ReportDto.kt new file mode 100644 index 0000000..1596fe3 --- /dev/null +++ b/app/src/main/java/com/isolaatti/reports/data/ReportDto.kt @@ -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 +) \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/reports/data/ReportEnums.kt b/app/src/main/java/com/isolaatti/reports/data/ReportEnums.kt new file mode 100644 index 0000000..9be598d --- /dev/null +++ b/app/src/main/java/com/isolaatti/reports/data/ReportEnums.kt @@ -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 +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/reports/data/ReportsApi.kt b/app/src/main/java/com/isolaatti/reports/data/ReportsApi.kt new file mode 100644 index 0000000..bed41d5 --- /dev/null +++ b/app/src/main/java/com/isolaatti/reports/data/ReportsApi.kt @@ -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 + + @GET("reports/by_me") + fun gerReportsByMe(): Call> +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/reports/data/ReportsRepositoryImpl.kt b/app/src/main/java/com/isolaatti/reports/data/ReportsRepositoryImpl.kt new file mode 100644 index 0000000..717162b --- /dev/null +++ b/app/src/main/java/com/isolaatti/reports/data/ReportsRepositoryImpl.kt @@ -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> = 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>> { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/reports/domain/ReportsRepository.kt b/app/src/main/java/com/isolaatti/reports/domain/ReportsRepository.kt new file mode 100644 index 0000000..9da7716 --- /dev/null +++ b/app/src/main/java/com/isolaatti/reports/domain/ReportsRepository.kt @@ -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> + + fun getReportsByMe(): Flow>> +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/reports/presentation/CreateReportViewModel.kt b/app/src/main/java/com/isolaatti/reports/presentation/CreateReportViewModel.kt new file mode 100644 index 0000000..91800e3 --- /dev/null +++ b/app/src/main/java/com/isolaatti/reports/presentation/CreateReportViewModel.kt @@ -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> = 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) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/reports/ui/NewReportBottomSheetDialogFragment.kt b/app/src/main/java/com/isolaatti/reports/ui/NewReportBottomSheetDialogFragment.kt index 7e07984..9e36e0e 100644 --- a/app/src/main/java/com/isolaatti/reports/ui/NewReportBottomSheetDialogFragment.kt +++ b/app/src/main/java/com/isolaatti/reports/ui/NewReportBottomSheetDialogFragment.kt @@ -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) + } + } } } } \ No newline at end of file diff --git a/app/src/main/res/layout/bottom_sheet_new_report.xml b/app/src/main/res/layout/bottom_sheet_new_report.xml index c48ec2e..ddb743b 100644 --- a/app/src/main/res/layout/bottom_sheet_new_report.xml +++ b/app/src/main/res/layout/bottom_sheet_new_report.xml @@ -22,27 +22,21 @@ app:layout_constraintTop_toBottomOf="@id/drag_handle"/> - - - - - + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + android:layout_marginTop="16dp" + android:layout_marginHorizontal="16dp"/> @@ -55,15 +49,17 @@ \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 910ea55..a00cad9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -205,4 +205,16 @@ Browse profiles v%s New report + Report submitted successfully + + Spam + Explicit content + Children at risk + Piracy + Copyright protected content + Hate and/or discrimination + Identity fraud + Fraudulent IA generated content + People in danger + \ No newline at end of file