From feea8ac7950f449ea0d513715391c952eb8547a0 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Thu, 11 Apr 2024 15:33:32 +0200 Subject: [PATCH] refactor: combine submitting and voting SponsorBlock segments into one dialog --- .../ui/dialogs/SubmitSegmentDialog.kt | 116 ++++++++++++++---- .../ui/dialogs/VoteForSegmentDialog.kt | 112 ----------------- .../main/res/layout/dialog_submit_segment.xml | 89 ++++++++++++-- .../main/res/layout/dialog_vote_segment.xml | 45 ------- app/src/main/res/values/style.xml | 4 +- 5 files changed, 172 insertions(+), 194 deletions(-) delete mode 100644 app/src/main/java/com/github/libretube/ui/dialogs/VoteForSegmentDialog.kt delete mode 100644 app/src/main/res/layout/dialog_vote_segment.xml diff --git a/app/src/main/java/com/github/libretube/ui/dialogs/SubmitSegmentDialog.kt b/app/src/main/java/com/github/libretube/ui/dialogs/SubmitSegmentDialog.kt index 235f582b4..47d6adccd 100644 --- a/app/src/main/java/com/github/libretube/ui/dialogs/SubmitSegmentDialog.kt +++ b/app/src/main/java/com/github/libretube/ui/dialogs/SubmitSegmentDialog.kt @@ -1,15 +1,17 @@ package com.github.libretube.ui.dialogs import android.app.Dialog -import android.content.DialogInterface import android.os.Bundle +import android.text.format.DateUtils import android.util.Log -import androidx.core.os.bundleOf +import android.widget.Toast import androidx.fragment.app.DialogFragment import androidx.lifecycle.lifecycleScope import com.github.libretube.BuildConfig import com.github.libretube.R +import com.github.libretube.api.JsonHelper import com.github.libretube.api.RetrofitInstance +import com.github.libretube.api.obj.Segment import com.github.libretube.constants.IntentData import com.github.libretube.databinding.DialogSubmitSegmentBinding import com.github.libretube.extensions.TAG @@ -20,11 +22,16 @@ import java.lang.Exception import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import kotlinx.serialization.encodeToString class SubmitSegmentDialog : DialogFragment() { private var videoId: String = "" private var currentPosition: Long = 0 private var duration: Long? = null + private var segments: List = emptyList() + + private var _binding: DialogSubmitSegmentBinding? = null + private val binding: DialogSubmitSegmentBinding get() = _binding!! override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -36,37 +43,34 @@ class SubmitSegmentDialog : DialogFragment() { } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val binding = DialogSubmitSegmentBinding.inflate(layoutInflater) + _binding = DialogSubmitSegmentBinding.inflate(layoutInflater) + + binding.createSegment.setOnClickListener { + lifecycleScope.launch { createSegment() } + } + binding.voteSegment.setOnClickListener { + lifecycleScope.launch { voteForSegment() } + } binding.startTime.setText((currentPosition.toFloat() / 1000).toString()) binding.segmentCategory.items = resources.getStringArray(R.array.sponsorBlockSegmentNames).toList() - return MaterialAlertDialogBuilder(requireContext()) - .setTitle(getString(R.string.sb_create_segment)) - .setView(binding.root) - .setPositiveButton(R.string.okay, null) - .setNegativeButton(R.string.cancel, null) - .setNeutralButton(R.string.vote_for_segment) { _, _ -> - VoteForSegmentDialog().apply { - arguments = bundleOf(IntentData.videoId to videoId) - }.show(parentFragmentManager, null) - } - .show() - .apply { - getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener { - requireDialog().hide() + lifecycleScope.launch(Dispatchers.IO) { + fetchSegments() + } - lifecycleScope.launch { - submitSegment(binding) - dismiss() - } - } - } + return MaterialAlertDialogBuilder(requireContext()) + .setView(binding.root) + .setNegativeButton(R.string.cancel, null) + .show() } - private suspend fun submitSegment(binding: DialogSubmitSegmentBinding) { + private suspend fun createSegment() { val context = requireContext().applicationContext + val binding = _binding ?: return + + requireDialog().hide() val startTime = binding.startTime.text.toString().toFloatOrNull() var endTime = binding.endTime.text.toString().toFloatOrNull() @@ -96,5 +100,69 @@ class SubmitSegmentDialog : DialogFragment() { Log.e(TAG(), e.toString()) context.toastFromMainDispatcher(e.localizedMessage.orEmpty()) } + + requireDialog().dismiss() + } + + private suspend fun voteForSegment() { + val binding = _binding ?: return + val context = requireContext().applicationContext + + val segmentID = segments.getOrNull(binding.segmentsDropdown.selectedItemPosition) + ?.uuid ?: return + + // see https://wiki.sponsor.ajay.app/w/API_Docs#POST_/api/voteOnSponsorTime + val score = when { + binding.upvote.isChecked -> 1 + binding.downvote.isChecked -> 0 + else -> 20 + } + + dialog?.hide() + lifecycleScope.launch(Dispatchers.IO) { + try { + RetrofitInstance.externalApi.voteOnSponsorTime( + uuid = segmentID, + userID = PreferenceHelper.getSponsorBlockUserID(), + score = score + ) + context.toastFromMainDispatcher(R.string.success) + } catch (e: Exception) { + context.toastFromMainDispatcher(e.localizedMessage.orEmpty()) + } + withContext(Dispatchers.Main) { dialog?.dismiss() } + } + } + + private suspend fun fetchSegments() { + val categories = resources.getStringArray(R.array.sponsorBlockSegments).toList() + segments = try { + RetrofitInstance.api.getSegments(videoId, JsonHelper.json.encodeToString(categories)).segments + } catch (e: Exception) { + Log.e(TAG(), e.toString()) + return + } + + withContext(Dispatchers.Main) { + val binding = _binding ?: return@withContext + + if (segments.isEmpty()) { + dismiss() + Toast.makeText(context, R.string.no_segments_found, Toast.LENGTH_SHORT).show() + return@withContext + } + + binding.segmentsDropdown.items = segments.map { + val (start, end) = it.segmentStartAndEnd + val (startStr, endStr) = DateUtils.formatElapsedTime(start.toLong()) to + DateUtils.formatElapsedTime(end.toLong()) + "${it.category} ($startStr - $endStr)" + } + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } } diff --git a/app/src/main/java/com/github/libretube/ui/dialogs/VoteForSegmentDialog.kt b/app/src/main/java/com/github/libretube/ui/dialogs/VoteForSegmentDialog.kt deleted file mode 100644 index c2820aeb0..000000000 --- a/app/src/main/java/com/github/libretube/ui/dialogs/VoteForSegmentDialog.kt +++ /dev/null @@ -1,112 +0,0 @@ -package com.github.libretube.ui.dialogs - -import android.app.Dialog -import android.content.DialogInterface -import android.os.Bundle -import android.text.format.DateUtils -import android.util.Log -import android.widget.Toast -import androidx.fragment.app.DialogFragment -import androidx.lifecycle.lifecycleScope -import com.github.libretube.R -import com.github.libretube.api.JsonHelper -import com.github.libretube.api.RetrofitInstance -import com.github.libretube.api.obj.Segment -import com.github.libretube.constants.IntentData -import com.github.libretube.databinding.DialogVoteSegmentBinding -import com.github.libretube.extensions.TAG -import com.github.libretube.extensions.toastFromMainDispatcher -import com.github.libretube.helpers.PreferenceHelper -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import kotlinx.serialization.encodeToString - -class VoteForSegmentDialog : DialogFragment() { - private lateinit var videoId: String - private var _binding: DialogVoteSegmentBinding? = null - private var segments: List = listOf() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - videoId = arguments?.getString(IntentData.videoId, "")!! - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - _binding = DialogVoteSegmentBinding.inflate(layoutInflater) - - lifecycleScope.launch(Dispatchers.IO) { - fetchSegments() - } - - return MaterialAlertDialogBuilder(requireContext()) - .setTitle(R.string.vote_for_segment) - .setView(_binding?.root) - .setNegativeButton(R.string.cancel, null) - .setPositiveButton(R.string.okay, null) - .show() - .apply { - getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener { - val binding = _binding ?: return@setOnClickListener - - val segmentID = segments.getOrNull(binding.segmentsDropdown.selectedItemPosition) - ?.uuid ?: return@setOnClickListener - - // see https://wiki.sponsor.ajay.app/w/API_Docs#POST_/api/voteOnSponsorTime - val score = when { - binding.upvote.isChecked -> 1 - binding.downvote.isChecked -> 0 - else -> 20 - } - - dialog?.hide() - lifecycleScope.launch(Dispatchers.IO) { - try { - RetrofitInstance.externalApi.voteOnSponsorTime( - uuid = segmentID, - userID = PreferenceHelper.getSponsorBlockUserID(), - score = score - ) - context.toastFromMainDispatcher(R.string.success) - } catch (e: Exception) { - context.toastFromMainDispatcher(e.localizedMessage.orEmpty()) - } - withContext(Dispatchers.Main) { dialog?.dismiss() } - } - } - } - } - - private suspend fun fetchSegments() { - val categories = resources.getStringArray(R.array.sponsorBlockSegments).toList() - segments = try { - RetrofitInstance.api.getSegments(videoId, JsonHelper.json.encodeToString(categories)).segments - } catch (e: Exception) { - Log.e(TAG(), e.toString()) - return - } - - withContext(Dispatchers.Main) { - val binding = _binding ?: return@withContext - - if (segments.isEmpty()) { - dismiss() - Toast.makeText(context, R.string.no_segments_found, Toast.LENGTH_SHORT).show() - return@withContext - } - - binding.segmentsDropdown.items = segments.map { - val (start, end) = it.segmentStartAndEnd - val (startStr, endStr) = DateUtils.formatElapsedTime(start.toLong()) to - DateUtils.formatElapsedTime(end.toLong()) - "${it.category} ($startStr - $endStr)" - } - } - } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } -} diff --git a/app/src/main/res/layout/dialog_submit_segment.xml b/app/src/main/res/layout/dialog_submit_segment.xml index 6ace23c9f..1f48697be 100644 --- a/app/src/main/res/layout/dialog_submit_segment.xml +++ b/app/src/main/res/layout/dialog_submit_segment.xml @@ -1,21 +1,29 @@ + android:orientation="vertical" + android:paddingHorizontal="15dp" + android:paddingTop="15dp"> + + + android:baselineAligned="false" + android:orientation="horizontal"> + android:layout_height="wrap_content" + android:layout_weight="1" + android:hint="@string/start_time"> + android:layout_height="wrap_content" + android:layout_marginStart="10dp" + android:layout_weight="1" + android:hint="@string/end_time"> + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_vote_segment.xml b/app/src/main/res/layout/dialog_vote_segment.xml deleted file mode 100644 index 3c2e57b7e..000000000 --- a/app/src/main/res/layout/dialog_vote_segment.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/style.xml b/app/src/main/res/values/style.xml index 3d0cd2c80..c73479e91 100644 --- a/app/src/main/res/values/style.xml +++ b/app/src/main/res/values/style.xml @@ -47,8 +47,8 @@ match_parent wrap_content - 15dp - 15dp + 15dp + 15dp 5dp 10dp