From ca5c7ec966da349fe5352962428d0eadbd91b9fe Mon Sep 17 00:00:00 2001 From: Ritika Pahwa <83745993+RitikaPahwa4444@users.noreply.github.com> Date: Sat, 21 Jun 2025 13:21:05 +0530 Subject: [PATCH 1/5] Bump up version code to 1053 --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2d9e213b2..9c53155a7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -24,8 +24,8 @@ android { applicationId = "fr.free.nrw.commons" minSdk = 21 targetSdk = 34 - versionCode = 1052 - versionName = "5.4.1" + versionCode = 1053 + versionName = "5.5.0" setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName()) testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" From 09da7b8d68169d1e9d6ca539b653d9aae7f50bb4 Mon Sep 17 00:00:00 2001 From: Sonal Yadav Date: Sun, 22 Jun 2025 19:10:15 +0530 Subject: [PATCH 2/5] Skip image upload to Wikidata (nearby -> green pins) (#6349) * Skip image upload to Wikidata if item already has image * Re-run CI * no more Failed to update Wikidata for green pins --- .../fr/free/nrw/commons/upload/worker/UploadWorker.kt | 8 +++++++- .../free/nrw/commons/wikidata/WikidataEditService.kt | 7 +++++-- .../main/res/values-x-invalidLanguageCode/error.xml | 10 ---------- 3 files changed, 12 insertions(+), 13 deletions(-) delete mode 100644 app/src/main/res/values-x-invalidLanguageCode/error.xml diff --git a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt index 6d28085b2..c8a1d9b98 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt @@ -472,7 +472,10 @@ class UploadWorker( if (wikiDataPlace != null) { if (!contribution.hasInvalidLocation()) { var revisionID: Long? = null + val p18WasSkipped = !wikiDataPlace.imageValue.isNullOrBlank() try { + if (!p18WasSkipped) { + // Only set P18 if the place does not already have a picture revisionID = wikidataEditService.createClaim( wikiDataPlace, @@ -489,9 +492,11 @@ class UploadWorker( .subscribeOn(Schedulers.io()) .blockingAwait() Timber.d("Updated WikiItem place ${place.name} with image ${place.pic}") + } } - showSuccessNotification(contribution) } + // Always show success notification, whether P18 was set or skipped + showSuccessNotification(contribution) } catch (exception: Exception) { Timber.e(exception) } @@ -500,6 +505,7 @@ class UploadWorker( wikidataEditService.handleImageClaimResult( contribution.wikidataPlace!!, revisionID, + p18WasSkipped = p18WasSkipped ) } } else { diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.kt index 0b49c03e9..f4bf23073 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.kt @@ -196,13 +196,16 @@ class WikidataEditService @Inject constructor( return wikidataClient.setClaim(claim, COMMONS_APP_TAG).blockingSingle() } - fun handleImageClaimResult(wikidataItem: WikidataItem, revisionId: Long?) { + fun handleImageClaimResult(wikidataItem: WikidataItem, revisionId: Long?, p18WasSkipped: Boolean = false) { if (revisionId != null) { wikidataEditListener?.onSuccessfulWikidataEdit() showSuccessToast(wikidataItem.name) - } else { + } else if (!p18WasSkipped) { Timber.d("Unable to make wiki data edit for entity %s", wikidataItem) showLongToast(context, context.getString(R.string.wikidata_edit_failure)) + } else { + Timber.d("Wikidata edit skipped for entity %s because P18 already exists", wikidataItem) + // No error shown to user, as this is not a failure } } diff --git a/app/src/main/res/values-x-invalidLanguageCode/error.xml b/app/src/main/res/values-x-invalidLanguageCode/error.xml deleted file mode 100644 index f4e2fe125..000000000 --- a/app/src/main/res/values-x-invalidLanguageCode/error.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - کامَنٕز گوو رُکِتھ - Oops. کیہہ تام گوو غلط! - ؤنِیوٚ اَسہِ توٚہہِ کیاہ ٲسِیوٚ کران، تہٕ کٕریٚو تہِ اَسہِ سٕتی شیر بذریعہِ برقی خط. یُس مَدَتھ کَرِ اَسہِ اَتھ شہَرنَس منٛز! - شُکریہ! - From d9e89174187d983aabdc9c0f3d1791b39e932538 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 23 Jun 2025 14:01:45 +0200 Subject: [PATCH 3/5] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-fi/strings.xml | 2 +- app/src/main/res/values-ps/strings.xml | 4 +- .../values-x-invalidLanguageCode/error.xml | 10 ++++ app/src/main/res/values-xmf/strings.xml | 46 ++++++++++++++++++- 4 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 app/src/main/res/values-x-invalidLanguageCode/error.xml diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index cdb25defc..e65f05825 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -180,7 +180,7 @@ Kuvaus: Sydneyn oopperatalo lahden toiselta puolelta katsottuna Luokat: Sydneyn oopperatalo lännestä katsottuna, Sydneyn oopperatalo kaukaa katsottuna Herätä Wikipedia-artikkelit eloon kuvillasi! Tuo kuvasi Wikipediaan. - Wikipedian kuvat tulevat Wikimedia Commonsista. + Wikipedian kuvat ovat peräisin Wikimedia Commonsista. Kuvasi auttavat useita ihmisiä ympäri maailmaa artikkeleiden ymmärtämisessä. Vältä tekijänoikeuksien alaista materiaalia, kuten julisteita, kirjan kansia ja useimpia Internetistä löydettyjä kuvia. Luuletko ymmärtäneesi tämän? diff --git a/app/src/main/res/values-ps/strings.xml b/app/src/main/res/values-ps/strings.xml index 6b3970c88..98bd626e9 100644 --- a/app/src/main/res/values-ps/strings.xml +++ b/app/src/main/res/values-ps/strings.xml @@ -136,7 +136,7 @@ %1$s مور ټولگې نه لري ويکي‌اوتوک خونديځ کې د خپلو انځورونو موندلو لپاره وېشنيزې ورگډې کړئ.\nوېشنيزو ورگډولو لپاره ټاپل پيل کړئ. وېشنيزې - امستنې + اوڼنې نومليکنه ټاکلی انځور دوديز ټاکونکی @@ -216,7 +216,7 @@ پورته کول نژدې په اړه - امستنې + اوڼنې غبرگون وتل بياکتنه diff --git a/app/src/main/res/values-x-invalidLanguageCode/error.xml b/app/src/main/res/values-x-invalidLanguageCode/error.xml new file mode 100644 index 000000000..f4e2fe125 --- /dev/null +++ b/app/src/main/res/values-x-invalidLanguageCode/error.xml @@ -0,0 +1,10 @@ + + + + کامَنٕز گوو رُکِتھ + Oops. کیہہ تام گوو غلط! + ؤنِیوٚ اَسہِ توٚہہِ کیاہ ٲسِیوٚ کران، تہٕ کٕریٚو تہِ اَسہِ سٕتی شیر بذریعہِ برقی خط. یُس مَدَتھ کَرِ اَسہِ اَتھ شہَرنَس منٛز! + شُکریہ! + diff --git a/app/src/main/res/values-xmf/strings.xml b/app/src/main/res/values-xmf/strings.xml index 153d88b54..3f9b0e59f 100644 --- a/app/src/main/res/values-xmf/strings.xml +++ b/app/src/main/res/values-xmf/strings.xml @@ -99,6 +99,8 @@ ფოტოშ გინოღალა გოხოლუას ჩქიმი ეხარგუეფი + რსხილიშ კოპირება + რსხილი კოპირებული რე ბუფერულ შვენას გობჟინაფა ფაილიშ ხასჷლაშ ძირაფა მუკნაჭარა (უციო) @@ -109,6 +111,7 @@ ძალამ მიარე უმწუძინუ ცადება. ქორთხინ, მუხირენ წუთშა ხოლო ქოცადით. მორდება, თე მახვარებუ ბლოკირი რე ვიკიოწკარუეს თქვა გემშიონათ ოკო ჟირფაქტორიანი ავტორიზაციაშ კოდი. + დოდასურაფაშ კოდი ჯღონელი რე თქვანი ელექტრონული ფოშტაშა. ქორთხიინთ, მოჯღონელი კოდი გენშეჸონათ მიშაულარო. მიშულაქ ვემიხუჯინუ ეხარგუა სერიაშ ჯოხო @@ -117,6 +120,7 @@ კატეგორიაშ გიშაგორუა დოგორით ელემენტეფი, ნამუეფით მოჩამილი რე თქვანი სურათის (გვალა, ტაჯ-მაჰალი დო თ.უ.) ჩუალა + გეძინელი მენიუ გოახალაფა ერკებული (ეხარგუეფი ვა რე) @@ -213,6 +217,7 @@ ბეტა ტესტირებას კათაფი ჩართით Beta-შა ჭირინაფა Google Play-ს დო მიღით ორდოიანი ჭირინაფა ახალ ფუნქციეფშა დო ჩილათეფიშ ეშახინტკალო 2ფა კოდი + ელექტრონული ფოშტაშ დოდასურაფაშ კოდი გოკონანო გიშულა? მედიაფაილიშ ჩილათა გიმენკატეგორიეფქ ვეგორინუ @@ -360,6 +365,7 @@ კინოხიანი სურათეფი ორენეფი + კატეგორიეფი მიკოწონებულეფშა გეძინა/ლასუა მიკოწონებულეფი თქვა ვეგეიძინჷნა აკა მიკოწონებული @@ -373,8 +379,10 @@ მოზოჯით ვიკიოწკარუეშა!\n\nგეხარგეთ თქვანი პირველი ფაილი, ქეგუნჭირით კონჭის გეძინა. კატეგორია ვა რე გიშაგორილი სურათეფი კატეგორიზაციაშ უმუშო შხირას მერკეთ გჷმორინაფონი რე. დასურო გონებჷნანო კატეგორიეფიშ მეწურაფაშ უმუშო გაგჷნძარაფა? + ეჭარუა ვა რე გიშაგორილი ეხარგუაშ გოუქვაფა ეხარგუაშ გოგჷნძორაფა + (ნაკორობაშ არძა სურათიშო) ათე არანს გორუა ალობაშ მოთხირი თენა კჷნ დღას ვაბკითხა @@ -383,9 +391,20 @@ ალობაშ მეჩამა გოუქვაფა ღოლამირჷ რე + თინას რენო კათეგორიეფი მეწურაფილი? + გეჸვენჯი სურათი + ქო, მუშენ ვარი ვა რე გუმორინაფილი სურათეფი + ვა რე ეხარგილი სურათეფი + თქვა ვაიღჷნა უკითხირუ გინაფეფი + ვა ვაიღჷნა კითხირებული გინაფეფი + ქოძირით თქვანი ელექტრონული ფოსტა + კითხირებულიშ ძირაფა + უკითხირებუშ ძირაფა სურათიშ ეხარგუაშ ბორჯის ჩილათაქ მოხვადჷ ქორთხინთ ქჷმიცადით … + კოპირაფილი რე + თე სურათიშ გიშატება ავტორი ორენი კამერაშ მოდელი @@ -393,9 +412,32 @@ სერიული ნომერი პროგრამული უნარღელჸუა სურათიშ ინფორმაცია - სელფი - ჸურილი + კატეგორიეფქ ვეგორინუ + ეჭარუეფქ ვეორინუ + ეხარგუაქ გეუქვუ + მუშენ დილასას ოკო %1$s-ქ? + %1$s ეხარგილი რე: %2$s + წუმოძინელო + %1$s ნომინირებული რე ოლასარო. + ვემიხუჯინუ + ვეშილებე ლასუაშ მოთხირი. + სელფი, ნამუთ ვეგჷმირინუაფუ ნამთინ სტატიას + ედომუშამო გინობარდილი რე + უაზრობა, აბსოლუტურო უმუხუჯუ რე ირნერი სტატიას + ახალი ამბეეფიშ სურათი + ნამდგა სურათი ინტერნეტიშე + ლოგო + პანორამაშ დუდიშულაშ აკორცუაფა თიშენ ნამჷ-და თინა რე + კატეგორიეფიშ მოახალებაშ ცადება. + კატეგორიაშ მოახალება + წუმოძინელო + + კატეგორია %1$s გეძინელი რე. + კატეგორიეფი %1$sგეძინელი რე. + + ვემიხუჯინუ კატეგორიეფიშ გეძინაქ. + კატეგორიეფიშ მოახალება მიკოწონებეფი პარამეტრეფი რუმე From 5d7f42d127600f205773214bb877472f259ff552 Mon Sep 17 00:00:00 2001 From: Rohit Verma <101377978+rohit9625@users.noreply.github.com> Date: Tue, 24 Jun 2025 18:26:00 +0530 Subject: [PATCH 4/5] Fix/file usage not working (#6354) * chore: add R8 rules to prevent obfuscating file usage classes * chore: upgrade lifecycle-runtime dependency to resolve lint errors * remove invalid resource directory --- app/proguard-rules.txt | 3 +++ .../main/res/values-x-invalidLanguageCode/error.xml | 10 ---------- gradle/libs.versions.toml | 2 +- 3 files changed, 4 insertions(+), 11 deletions(-) delete mode 100644 app/src/main/res/values-x-invalidLanguageCode/error.xml diff --git a/app/proguard-rules.txt b/app/proguard-rules.txt index 63981633b..21c584ba9 100644 --- a/app/proguard-rules.txt +++ b/app/proguard-rules.txt @@ -66,6 +66,9 @@ # Application classes that will be serialized/deserialized over Gson -keep class com.google.gson.examples.android.model.** { *; } +# Prevent R8 from obfuscating project classes used by Gson for parsing +-keep class fr.free.nrw.commons.fileusages.** { *; } + # Prevent proguard from stripping interface information from TypeAdapterFactory, # JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter) -keep class * implements com.google.gson.TypeAdapterFactory diff --git a/app/src/main/res/values-x-invalidLanguageCode/error.xml b/app/src/main/res/values-x-invalidLanguageCode/error.xml deleted file mode 100644 index f4e2fe125..000000000 --- a/app/src/main/res/values-x-invalidLanguageCode/error.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - کامَنٕز گوو رُکِتھ - Oops. کیہہ تام گوو غلط! - ؤنِیوٚ اَسہِ توٚہہِ کیاہ ٲسِیوٚ کران، تہٕ کٕریٚو تہِ اَسہِ سٕتی شیر بذریعہِ برقی خط. یُس مَدَتھ کَرِ اَسہِ اَتھ شہَرنَس منٛز! - شُکریہ! - diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index afb3615d2..f38357b04 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,7 +39,7 @@ leakcanary = "2.10" livedataTesting = "1.2.0" swipelayout = "1.2.0" viewpagerIndicator = "2.4.1.1" -lifecycleRuntimeKtx = "2.8.4" +lifecycleRuntimeKtx = "2.8.7" loggingInterceptor = "4.10.0" logbackAndroidClassic = "1.1.1-6" material = "1.12.0" From ad7dddaac439b3af4332e43552fc25994a74f219 Mon Sep 17 00:00:00 2001 From: Rohit Verma <101377978+rohit9625@users.noreply.github.com> Date: Wed, 25 Jun 2025 08:54:03 +0530 Subject: [PATCH 5/5] Fix infinite loading circular progress bar after nominating for deletion (#6324) * fix: infinite loading progress bar after nominating for deletion * add logs for testing * refactor: use globalFileUsage instead of achievement to append in reason Fetching achievements is a time consuming operation and globalFileUsage gives the similar result in optimal time * test(ReasonBuilder): fix tests according to new behavior * refactor: remove logs added for testing * test: await for async getReason method call --------- Co-authored-by: Neel Doshi Co-authored-by: Nicolas Raoul --- app/build.gradle.kts | 1 + .../free/nrw/commons/delete/DeleteHelper.kt | 2 - .../free/nrw/commons/delete/ReasonBuilder.kt | 51 +++++++----- .../nrw/commons/media/MediaDetailFragment.kt | 79 +++++++++++-------- .../nrw/commons/mwapi/OkHttpJsonApiClient.kt | 1 + app/src/main/res/values/strings.xml | 3 +- .../nrw/commons/delete/ReasonBuilderTest.kt | 15 ++-- gradle/libs.versions.toml | 2 + 8 files changed, 88 insertions(+), 66 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9c53155a7..2e391a24f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -347,6 +347,7 @@ dependencies { // Kotlin + coroutines implementation(libs.androidx.work.runtime.ktx) implementation(libs.androidx.work.runtime) + implementation(libs.kotlinx.coroutines.rx2) testImplementation(libs.androidx.work.testing) //Glide diff --git a/app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.kt b/app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.kt index 09959d0ef..3f9ae47ac 100644 --- a/app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.kt +++ b/app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.kt @@ -53,7 +53,6 @@ class DeleteHelper @Inject constructor( media: Media?, reason: String? ): Single? { - if(context == null && media == null) { return null } @@ -86,7 +85,6 @@ class DeleteHelper @Inject constructor( * @return */ private fun delete(media: Media, reason: String): Observable { - Timber.d("thread is delete %s", Thread.currentThread().name) val summary = "Nominating ${media.filename} for deletion." val calendar = Calendar.getInstance() val fileDeleteString = """ diff --git a/app/src/main/java/fr/free/nrw/commons/delete/ReasonBuilder.kt b/app/src/main/java/fr/free/nrw/commons/delete/ReasonBuilder.kt index 09018c249..c4cd73b8a 100644 --- a/app/src/main/java/fr/free/nrw/commons/delete/ReasonBuilder.kt +++ b/app/src/main/java/fr/free/nrw/commons/delete/ReasonBuilder.kt @@ -2,21 +2,19 @@ package fr.free.nrw.commons.delete import android.annotation.SuppressLint import android.content.Context - -import fr.free.nrw.commons.utils.DateUtil -import java.util.Locale - -import javax.inject.Inject -import javax.inject.Singleton - import fr.free.nrw.commons.Media import fr.free.nrw.commons.R -import fr.free.nrw.commons.profile.achievements.FeedbackResponse import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient +import fr.free.nrw.commons.utils.DateUtil import fr.free.nrw.commons.utils.ViewUtilWrapper import io.reactivex.Single +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.rx2.rxSingle import timber.log.Timber +import java.util.Locale +import javax.inject.Inject +import javax.inject.Singleton /** * This class handles the reason for deleting a Media object @@ -29,6 +27,8 @@ class ReasonBuilder @Inject constructor( private val viewUtilWrapper: ViewUtilWrapper ) { + private val defaultFileUsagePageSize = 10 + /** * To process the reason and append the media's upload date and uploaded_by_me string * @param media @@ -39,7 +39,7 @@ class ReasonBuilder @Inject constructor( if (media == null || reason == null) { return Single.just("Not known") } - return fetchArticleNumber(media, reason) + return getAndAppendFileUsage(media, reason) } /** @@ -54,27 +54,36 @@ class ReasonBuilder @Inject constructor( } } - private fun fetchArticleNumber(media: Media, reason: String): Single { - return if (checkAccount()) { - okHttpJsonApiClient - .getAchievements(sessionManager.userName) - .map { feedbackResponse -> appendArticlesUsed(feedbackResponse, media, reason) } - } else { - Single.just("") + private fun getAndAppendFileUsage(media: Media, reason: String): Single { + return rxSingle(context = Dispatchers.IO) { + if (!checkAccount()) return@rxSingle "" + + try { + val globalFileUsage = okHttpJsonApiClient.getGlobalFileUsages( + fileName = media.filename, + pageSize = defaultFileUsagePageSize + ) + val globalUsages = globalFileUsage?.query?.pages?.sumOf { it.fileUsage.size } ?: 0 + + appendArticlesUsed(globalUsages, media, reason) + } catch (e: Exception) { + Timber.e(e, "Error fetching file usage") + throw e + } } } /** - * Takes the uploaded_by_me string, the upload date, name of articles using images + * Takes the uploaded_by_me string, the upload date, no. of articles using images * and appends it to the received reason - * @param feedBack object + * @param fileUsages No. of files/articles using this image * @param media whose upload data is to be fetched - * @param reason + * @param reason string to be appended */ @SuppressLint("StringFormatInvalid") - private fun appendArticlesUsed(feedBack: FeedbackResponse, media: Media, reason: String): String { + private fun appendArticlesUsed(fileUsages: Int, media: Media, reason: String): String { val reason1Template = context.getString(R.string.uploaded_by_myself) - return reason + String.format(Locale.getDefault(), reason1Template, prettyUploadedDate(media), feedBack.articlesUsingImages) + return reason + String.format(Locale.getDefault(), reason1Template, prettyUploadedDate(media), fileUsages) .also { Timber.i("New Reason %s", it) } } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt index 8a4d530c4..f371b733f 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt @@ -74,7 +74,6 @@ import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.CameraPosition import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.CommonsApplication.Companion.instance -import fr.free.nrw.commons.locationpicker.LocationPicker import fr.free.nrw.commons.Media import fr.free.nrw.commons.MediaDataExtractor import fr.free.nrw.commons.R @@ -102,6 +101,7 @@ import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.language.AppLanguageLookUpTable import fr.free.nrw.commons.location.LocationServiceManager +import fr.free.nrw.commons.locationpicker.LocationPicker import fr.free.nrw.commons.media.MediaDetailPagerFragment.MediaDetailProvider import fr.free.nrw.commons.profile.ProfileActivity import fr.free.nrw.commons.review.ReviewHelper @@ -116,6 +116,7 @@ import fr.free.nrw.commons.utils.LangCodeUtils.getLocalizedResources import fr.free.nrw.commons.utils.PermissionUtils.PERMISSIONS_STORAGE import fr.free.nrw.commons.utils.PermissionUtils.checkPermissionsAndPerformAction import fr.free.nrw.commons.utils.PermissionUtils.hasPermission +import fr.free.nrw.commons.utils.ViewUtil import fr.free.nrw.commons.utils.ViewUtil.showShortToast import fr.free.nrw.commons.utils.ViewUtilWrapper import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage.Revision @@ -125,6 +126,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers import org.apache.commons.lang3.StringUtils import timber.log.Timber +import java.lang.String.format import java.util.Date import java.util.Locale import java.util.Objects @@ -1646,7 +1648,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C getString(R.string.cancel), { val reason: String = input.text.toString() - onDeleteClickeddialogtext(reason) + onDeleteClickedDialogText(reason) }, {}, input @@ -1700,26 +1702,48 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C resultSingle .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe { _ -> - if (applicationKvStore.getBoolean( - String.format( - NOMINATING_FOR_DELETION_MEDIA, media!!.imageUrl - ), false - ) - ) { - applicationKvStore.remove( - String.format( - NOMINATING_FOR_DELETION_MEDIA, - media!!.imageUrl - ) - ) - callback!!.nominatingForDeletion(index) - } - } + .subscribe(this::handleDeletionResult, this::handleDeletionError); + } + + /** + * Disables Progress Bar and Update delete button text. + */ + private fun disableProgressBar() { + activity?.run { + runOnUiThread(Runnable { + binding.progressBarDeletion.visibility = View.GONE + }) + } ?: return // Prevent NullPointerException when fragment is not attached to activity + } + + private fun handleDeletionResult(success: Boolean) { + if (success) { + binding.nominateDeletion.text = getString(R.string.nominated_for_deletion_btn) + ViewUtil.showLongSnackbar(requireView(), getString(R.string.nominated_for_deletion)) + disableProgressBar() + checkAndClearDeletionFlag() + } else { + disableProgressBar() + } + } + + private fun handleDeletionError(throwable: Throwable) { + throwable.printStackTrace() + disableProgressBar() + checkAndClearDeletionFlag() + } + + private fun checkAndClearDeletionFlag() { + if (applicationKvStore + .getBoolean(format(NOMINATING_FOR_DELETION_MEDIA, media!!.imageUrl), false) + ) { + applicationKvStore.remove(format(NOMINATING_FOR_DELETION_MEDIA, media!!.imageUrl)) + callback!!.nominatingForDeletion(index) + } } @SuppressLint("CheckResult") - private fun onDeleteClickeddialogtext(reason: String) { + private fun onDeleteClickedDialogText(reason: String) { applicationKvStore.putBoolean( String.format( NOMINATING_FOR_DELETION_MEDIA, @@ -1736,22 +1760,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C resultSingletext .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe { _ -> - if (applicationKvStore.getBoolean( - String.format( - NOMINATING_FOR_DELETION_MEDIA, media!!.imageUrl - ), false - ) - ) { - applicationKvStore.remove( - String.format( - NOMINATING_FOR_DELETION_MEDIA, - media!!.imageUrl - ) - ) - callback!!.nominatingForDeletion(index) - } - } + .subscribe(this::handleDeletionResult, this::handleDeletionError); } private fun onSeeMoreClicked() { diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/OkHttpJsonApiClient.kt b/app/src/main/java/fr/free/nrw/commons/mwapi/OkHttpJsonApiClient.kt index 4fa7979d2..a2f92c2e6 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/OkHttpJsonApiClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/OkHttpJsonApiClient.kt @@ -281,6 +281,7 @@ class OkHttpJsonApiClient @Inject constructor( FeedbackResponse::class.java ) } catch (e: Exception) { + e.printStackTrace() return@fromCallable FeedbackResponse(0, 0, 0, FeaturedImages(0, 0), 0, "") } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 68dba88be..c9a2d16c8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -431,7 +431,7 @@ I changed my mind, I don\'t want it to be publicly visible anymore Sorry this picture is not interesting for an encyclopedia - Uploaded by myself on %1$s, used in %2$d article(s). + Uploaded by myself on %1$s, used in %2$d article(s) at least. Welcome to Commons!\n Upload your first media by tapping on the add button. @@ -876,4 +876,5 @@ Upload your first media by tapping on the add button. Show in Nearby Created and uploaded by: %1$s Created by %1$s and uploaded by %2$s + Nominated for Deletion diff --git a/app/src/test/kotlin/fr/free/nrw/commons/delete/ReasonBuilderTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/delete/ReasonBuilderTest.kt index e89a02dec..bd67475ee 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/delete/ReasonBuilderTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/delete/ReasonBuilderTest.kt @@ -5,13 +5,14 @@ import android.content.res.Resources import fr.free.nrw.commons.Media import fr.free.nrw.commons.R import fr.free.nrw.commons.auth.SessionManager +import fr.free.nrw.commons.fileusages.GlobalFileUsagesResponse import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient -import fr.free.nrw.commons.profile.achievements.FeedbackResponse import fr.free.nrw.commons.profile.leaderboard.LeaderboardResponse import fr.free.nrw.commons.profile.leaderboard.UpdateAvatarResponse import fr.free.nrw.commons.utils.ViewUtilWrapper import io.reactivex.Observable import io.reactivex.Single +import kotlinx.coroutines.test.runTest import media import org.junit.Before import org.junit.Test @@ -58,16 +59,16 @@ class ReasonBuilderTest { PowerMockito.`when`(context?.getString(R.string.user_not_logged_in)) .thenReturn("Log-in expired. Please log in again.") - reasonBuilder!!.getReason(mock(Media::class.java), "test") + reasonBuilder!!.getReason(mock(Media::class.java), "test").test().await() verify(sessionManager, times(1))!!.forceLogin(any(Context::class.java)) } @Test - fun getReason() { + fun getReason() = runTest { `when`(sessionManager?.userName).thenReturn("Testuser") `when`(sessionManager?.doesAccountExist()).thenReturn(true) - `when`(okHttpJsonApiClient!!.getAchievements(anyString())) - .thenReturn(Single.just(mock(FeedbackResponse::class.java))) + `when`(okHttpJsonApiClient!!.getGlobalFileUsages(anyString(), anyInt())) + .thenReturn(mock(GlobalFileUsagesResponse::class.java)) `when`(okHttpJsonApiClient!!.getLeaderboard(anyString(), anyString(), anyString(), anyString(), anyString())) .thenReturn(Observable.just(mock(LeaderboardResponse::class.java))) `when`(okHttpJsonApiClient!!.setAvatar(anyString(), anyString())) @@ -75,8 +76,8 @@ class ReasonBuilderTest { val media = media(filename = "test_file", dateUploaded = Date()) - reasonBuilder!!.getReason(media, "test") + reasonBuilder!!.getReason(media, "test").test().await() verify(sessionManager, times(0))!!.forceLogin(any(Context::class.java)) - verify(okHttpJsonApiClient, times(1))!!.getAchievements(anyString()) + verify(okHttpJsonApiClient, times(1))!!.getGlobalFileUsages(anyString(), anyInt()) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f38357b04..df4b7bb43 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -16,6 +16,7 @@ constraintlayout = "1.1.3" coordinates2country = "1.8" dexcount = "4.0.0" githubTripletPlay = "2.7.2" +kotlinxCoroutinesRx2 = "1.8.0" osmdroidAndroid = "6.1.17" testCore = "1.4.0" coreKtx = "1.9.0" @@ -127,6 +128,7 @@ dagger-compiler = { module = "com.google.dagger:dagger-compiler", version.ref = facebook-fresco = { module = "com.facebook.fresco:fresco", version.ref = "frescoVersion" } glide-compiler = { module = "com.github.bumptech.glide:compiler", version.ref = "glide" } glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" } +kotlinx-coroutines-rx2 = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-rx2", version.ref = "kotlinxCoroutinesRx2" } photoview = { module = "com.github.chrisbanes:PhotoView", version.ref = "photoviewVersion" } # RxJava and Reactive Programming