From 1c6ebafb2966653c20cda69b109dd1981bbbb85f Mon Sep 17 00:00:00 2001 From: yuvraj-coder1 <142040957+yuvraj-coder1@users.noreply.github.com> Date: Sat, 18 Jan 2025 19:34:28 +0530 Subject: [PATCH] feat: show the proper message in the custom picker when all images are either uploaded or marked. (#6095) * feat: show the message "Congratulations, all pictures in this album have been either uploaded or marked as not for upload." in the custom picker when all images are either uploaded or marked. * refactor: fix indentation in the code * refactor: replace LiveData with StateFlow * fix: fixed the bug that was causing one ImageFragment testcase to fail. * fix: fixed the Congratulation message being shown for a brief moment while switching off the switch * refactor: move hardcoded string to strings.xml. * docs: add comment to clarify visibility logic in ImageFragment --------- Co-authored-by: Nicolas Raoul --- .../customselector/ui/adapter/ImageAdapter.kt | 27 +++++++++++- .../ui/selector/ImageFragment.kt | 42 ++++++++++++++++++- .../res/layout/fragment_custom_selector.xml | 14 +++++++ app/src/main/res/values/strings.xml | 1 + 4 files changed, 81 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt index 74b937f97..ff623d496 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt @@ -24,6 +24,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.MainScope import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import java.util.TreeMap import kotlin.collections.ArrayList @@ -103,6 +104,18 @@ class ImageAdapter( */ private var imagePositionAsPerIncreasingOrder = 0 + /** + * Stores the number of images currently visible on the screen + */ + private val _currentImagesCount = MutableStateFlow(0) + val currentImagesCount = _currentImagesCount + + /** + * Stores whether images are being loaded or not + */ + private val _isLoadingImages = MutableStateFlow(false) + val isLoadingImages = _isLoadingImages + /** * Coroutine Dispatchers and Scope. */ @@ -184,8 +197,12 @@ class ImageAdapter( // If the position is not already visited, that means the position is new then // finds the next actionable image position from all images if (!alreadyAddedPositions.contains(position)) { - processThumbnailForActionedImage(holder, position, uploadingContributionList) - + processThumbnailForActionedImage( + holder, + position, + uploadingContributionList + ) + _isLoadingImages.value = false // If the position is already visited, that means the image is already present // inside map, so it will fetch the image from the map and load in the holder } else { @@ -231,6 +248,7 @@ class ImageAdapter( position: Int, uploadingContributionList: List, ) { + _isLoadingImages.value = true val next = imageLoader.nextActionableImage( allImages, @@ -252,6 +270,7 @@ class ImageAdapter( actionableImagesMap[next] = allImages[next] alreadyAddedPositions.add(imagePositionAsPerIncreasingOrder) imagePositionAsPerIncreasingOrder++ + _currentImagesCount.value = imagePositionAsPerIncreasingOrder Glide .with(holder.image) .load(allImages[next].uri) @@ -267,6 +286,7 @@ class ImageAdapter( reachedEndOfFolder = true notifyItemRemoved(position) } + _isLoadingImages.value = false } /** @@ -372,6 +392,7 @@ class ImageAdapter( emptyMap: TreeMap, uploadedImages: List = ArrayList(), ) { + _isLoadingImages.value = true allImages = fixedImages val oldImageList: ArrayList = images val newImageList: ArrayList = ArrayList(newImages) @@ -382,6 +403,7 @@ class ImageAdapter( reachedEndOfFolder = false selectedImages = ArrayList() imagePositionAsPerIncreasingOrder = 0 + _currentImagesCount.value = imagePositionAsPerIncreasingOrder val diffResult = DiffUtil.calculateDiff( ImagesDiffCallback(oldImageList, newImageList), @@ -441,6 +463,7 @@ class ImageAdapter( val entry = iterator.next() if (entry.value == image) { imagePositionAsPerIncreasingOrder -= 1 + _currentImagesCount.value = imagePositionAsPerIncreasingOrder iterator.remove() alreadyAddedPositions.removeAt(alreadyAddedPositions.size - 1) notifyItemRemoved(index) diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt index 3912a4d12..39d0d545a 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt @@ -12,8 +12,12 @@ import android.widget.ProgressBar import android.widget.Switch import androidx.appcompat.app.AlertDialog import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.isVisible +import androidx.lifecycle.Lifecycle import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import fr.free.nrw.commons.contributions.Contribution @@ -38,6 +42,10 @@ import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.upload.FileProcessor import fr.free.nrw.commons.upload.FileUtilsWrapper import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.launch import java.util.TreeMap import javax.inject.Inject import kotlin.collections.ArrayList @@ -80,6 +88,12 @@ class ImageFragment : */ var allImages: ArrayList = ArrayList() + /** + * Keeps track of switch state + */ + private val _switchState = MutableStateFlow(false) + val switchState = _switchState.asStateFlow() + /** * View model Factory. */ @@ -214,7 +228,11 @@ class ImageFragment : switch = binding?.switchWidget switch?.visibility = View.VISIBLE - switch?.setOnCheckedChangeListener { _, isChecked -> onChangeSwitchState(isChecked) } + _switchState.value = switch?.isChecked ?: false + switch?.setOnCheckedChangeListener { _, isChecked -> + onChangeSwitchState(isChecked) + _switchState.value = isChecked + } selectorRV = binding?.selectorRv loader = binding?.loader progressLayout = binding?.progressLayout @@ -234,6 +252,28 @@ class ImageFragment : return binding?.root } + /** + * onViewCreated + * Updates empty text view visibility based on image count, switch state, and loading status. + */ + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + combine( + imageAdapter.currentImagesCount, + switchState, + imageAdapter.isLoadingImages + ) { imageCount, isChecked, isLoadingImages -> + Triple(imageCount, isChecked, isLoadingImages) + }.collect { (imageCount, isChecked, isLoadingImages) -> + binding?.allImagesUploadedOrMarked?.isVisible = + !isLoadingImages && !isChecked && imageCount == 0 && (switch?.isVisible == true) + } + } + } + } + private fun onChangeSwitchState(checked: Boolean) { if (checked) { showAlreadyActionedImages = true diff --git a/app/src/main/res/layout/fragment_custom_selector.xml b/app/src/main/res/layout/fragment_custom_selector.xml index bbc4c0a07..03381fd24 100644 --- a/app/src/main/res/layout/fragment_custom_selector.xml +++ b/app/src/main/res/layout/fragment_custom_selector.xml @@ -49,6 +49,20 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + last resort and should only be used when you wish to stop editing forever and also to hide as many of your past associations as possible.

Account deletion on Wikimedia Commons is done by changing your account name to make it so others cannot recognize your contributions in a process called account vanishing. Vanishing does not guarantee complete anonymity or remove contributions to the projects.]]>
Caption Caption copied to clipboard + Congratulations, all pictures in this album have been either uploaded or marked as not for upload.