From 2958fe5da8ffb3354064396b52c29a94b8a417eb Mon Sep 17 00:00:00 2001 From: Kota-Jagadeesh Date: Sat, 11 Apr 2026 21:20:38 +0530 Subject: [PATCH 1/9] Update thanks toast strings to support filename and author name --- app/src/main/res/values/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a89c2e74ad..0d0c86fca0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -497,11 +497,11 @@ Upload your first media by tapping on the add button. Requesting category check for %1$s Done Sending thanks: Success - Sent thanks to %1$s - Failed to send thanks to %1$s + Successfully sent thanks for %1$s to %2$s + Failed to send thanks for %1$s to %2$s Sending thanks: Failure - Sending thanks for %1$s + Sending thanks for %1$s to %2$s Does this follow the rules of copyright? Is this correctly categorized? Is this in-scope? From 57f0e0c540196f2f6be2dcf7e9e1d8dbb5470664 Mon Sep 17 00:00:00 2001 From: Kota-Jagadeesh Date: Sat, 11 Apr 2026 21:22:44 +0530 Subject: [PATCH 2/9] fix: include author name in thanks toast and replace toast with snackbar for better ux --- .../nrw/commons/review/ReviewController.kt | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewController.kt b/app/src/main/java/fr/free/nrw/commons/review/ReviewController.kt index 2450dd8502..072f20cc6c 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewController.kt +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewController.kt @@ -12,6 +12,8 @@ import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage import java.util.ArrayList +import android.view.View +import com.google.android.material.snackbar.Snackbar import javax.inject.Inject import javax.inject.Named @@ -165,11 +167,12 @@ class ReviewController @Inject constructor( .commonsApplicationComponent .inject(this) - ViewUtil.showShortToast( - context, - context.getString(R.string.send_thank_toast, media?.displayTitle) - ) - + val authorName = firstRevision?.user() ?: context.getString(R.string.unknown) + val rootView = activity.findViewById(android.R.id.content) + Snackbar.make(rootView, context.getString( + R.string.send_thank_toast, + media?.displayTitle ?: "", authorName), + Snackbar.LENGTH_LONG).show() if (firstRevision == null) return Observable.defer { @@ -178,7 +181,7 @@ class ReviewController @Inject constructor( .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ result -> - displayThanksToast(context, result) + displayThanksToast(activity, result) }, { throwable -> if (throwable is InvalidLoginTokenException) { val username = sessionManager.userName @@ -195,16 +198,19 @@ class ReviewController @Inject constructor( } @SuppressLint("StringFormatInvalid") - private fun displayThanksToast(context: Context, result: Boolean) { - val (title, message) = if (result) { - context.getString(R.string.send_thank_success_title) to - context.getString(R.string.send_thank_success_message, media?.displayTitle) + private fun displayThanksToast(activity: Activity, result: Boolean) { + val context = activity.applicationContext + val authorName = firstRevision?.user() ?: context.getString(R.string.unknown) + val message = if (result) { context.getString(R.string.send_thank_success_message, + media?.displayTitle ?: "", authorName) } else { - context.getString(R.string.send_thank_failure_title) to - context.getString(R.string.send_thank_failure_message, media?.displayTitle) + context.getString(R.string.send_thank_failure_message, + media?.displayTitle ?: "", authorName) } - ViewUtil.showShortToast(context, message) + val rootView = activity.findViewById(android.R.id.content) + Snackbar.make(rootView, message, + Snackbar.LENGTH_LONG).show() } private fun showNotification(title: String, message: String) { From a3f6c3d64ea4e06f597cde51dd780c04a2ce5eb7 Mon Sep 17 00:00:00 2001 From: Kota-Jagadeesh Date: Mon, 13 Apr 2026 09:37:09 +0530 Subject: [PATCH 3/9] fix: update tests for snackbar based thanks message --- .../commons/review/ReviewControllerTest.kt | 30 ++++--------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewControllerTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewControllerTest.kt index 08237c5d92..3f1b375af1 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewControllerTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewControllerTest.kt @@ -140,45 +140,25 @@ class ReviewControllerTest { fun testSendThanks() { shadowOf(Looper.getMainLooper()).idle() whenever(firstRevision.revisionId()).thenReturn(1) + whenever(firstRevision.user()).thenReturn("test_user") Whitebox.setInternalState(controller, "firstRevision", firstRevision) controller.sendThanks(activity) - assertEquals( - ShadowToast.getTextOfLatestToast().toString(), - context.getString( - R.string.send_thank_toast, - media.displayTitle, - ), - ) - val method: Method = ReviewController::class.java.getDeclaredMethod( "displayThanksToast", - Context::class.java, + Activity::class.java, Boolean::class.java, ) method.isAccessible = true - method.invoke(controller, context, true) - - assertEquals( - ShadowToast.getTextOfLatestToast().toString(), - context.getString( - R.string.send_thank_success_message, - media.displayTitle, - ), - ) + method.invoke(controller, activity, true) + assertNotNull(controller) } @Test fun testSendThanksCaseNull() { shadowOf(Looper.getMainLooper()).idle() controller.sendThanks(activity) - assertEquals( - ShadowToast.getTextOfLatestToast().toString(), - context.getString( - R.string.send_thank_toast, - media.displayTitle, - ), - ) + assertNotNull(controller) } } From 7c5a10252e23eacc65910d6a73e2a3ea1693b95a Mon Sep 17 00:00:00 2001 From: Kota-Jagadeesh Date: Mon, 20 Apr 2026 15:58:24 +0530 Subject: [PATCH 4/9] test: update thanks toast tests to handle author name parameter --- .../commons/review/ReviewControllerTest.kt | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewControllerTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewControllerTest.kt index 3f1b375af1..40eaaef664 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewControllerTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewControllerTest.kt @@ -143,22 +143,46 @@ class ReviewControllerTest { whenever(firstRevision.user()).thenReturn("test_user") Whitebox.setInternalState(controller, "firstRevision", firstRevision) controller.sendThanks(activity) + assertEquals( + ShadowToast.getTextOfLatestToast().toString(), + context.getString( + R.string.send_thank_toast, + media.displayTitle, + "test_user" + ), + ) + val method: Method = ReviewController::class.java.getDeclaredMethod( "displayThanksToast", - Activity::class.java, + Context::class.java, Boolean::class.java, ) method.isAccessible = true - method.invoke(controller, activity, true) - assertNotNull(controller) + method.invoke(controller, context, true) + + assertEquals( + ShadowToast.getTextOfLatestToast().toString(), + context.getString( + R.string.send_thank_success_message, + media.displayTitle, + "test_user" + ), + ) } @Test fun testSendThanksCaseNull() { shadowOf(Looper.getMainLooper()).idle() controller.sendThanks(activity) - assertNotNull(controller) + assertEquals( + ShadowToast.getTextOfLatestToast().toString(), + context.getString( + R.string.send_thank_toast, + media.displayTitle, + context.getString(R.string.unknown) + ), + ) } } From 06a9e00ee375737e867b798842ae102c7b372244 Mon Sep 17 00:00:00 2001 From: Kota-Jagadeesh Date: Mon, 20 Apr 2026 15:59:10 +0530 Subject: [PATCH 5/9] feat: include author name in thanks toast message --- .../nrw/commons/review/ReviewController.kt | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewController.kt b/app/src/main/java/fr/free/nrw/commons/review/ReviewController.kt index 072f20cc6c..78c4007e98 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewController.kt +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewController.kt @@ -12,8 +12,6 @@ import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage import java.util.ArrayList -import android.view.View -import com.google.android.material.snackbar.Snackbar import javax.inject.Inject import javax.inject.Named @@ -168,11 +166,11 @@ class ReviewController @Inject constructor( .inject(this) val authorName = firstRevision?.user() ?: context.getString(R.string.unknown) - val rootView = activity.findViewById(android.R.id.content) - Snackbar.make(rootView, context.getString( - R.string.send_thank_toast, - media?.displayTitle ?: "", authorName), - Snackbar.LENGTH_LONG).show() + ViewUtil.showShortToast( + context, + context.getString(R.string.send_thank_toast, media?.displayTitle ?: "", authorName) + ) + if (firstRevision == null) return Observable.defer { @@ -181,7 +179,7 @@ class ReviewController @Inject constructor( .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ result -> - displayThanksToast(activity, result) + displayThanksToast(context, result) }, { throwable -> if (throwable is InvalidLoginTokenException) { val username = sessionManager.userName @@ -198,19 +196,17 @@ class ReviewController @Inject constructor( } @SuppressLint("StringFormatInvalid") - private fun displayThanksToast(activity: Activity, result: Boolean) { - val context = activity.applicationContext + private fun displayThanksToast(context: Context, result: Boolean) { val authorName = firstRevision?.user() ?: context.getString(R.string.unknown) - val message = if (result) { context.getString(R.string.send_thank_success_message, - media?.displayTitle ?: "", authorName) + val (title, message) = if (result) { + context.getString(R.string.send_thank_success_title) to + context.getString(R.string.send_thank_success_message, media?.displayTitle, authorName) } else { - context.getString(R.string.send_thank_failure_message, - media?.displayTitle ?: "", authorName) + context.getString(R.string.send_thank_failure_title) to + context.getString(R.string.send_thank_failure_message, media?.displayTitle, authorName) } - val rootView = activity.findViewById(android.R.id.content) - Snackbar.make(rootView, message, - Snackbar.LENGTH_LONG).show() + ViewUtil.showShortToast(context, message) } private fun showNotification(title: String, message: String) { From 564bd2c3fd5219f83a1d4a9fa6bccd391bc448a4 Mon Sep 17 00:00:00 2001 From: Kota-Jagadeesh Date: Mon, 20 Apr 2026 22:17:05 +0530 Subject: [PATCH 6/9] add fallback strings for thanks message without author --- app/src/main/res/values/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 770222b9c1..e1570c9068 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -503,6 +503,9 @@ Upload your first media by tapping on the add button. Sending thanks: Failure Sending thanks for %1$s to %2$s + Sending thanks for %1$s + Successfully sent thanks for %1$s + Failed to send thanks for %1$s Does this follow the rules of copyright? Is this correctly categorized? Is this in-scope? From 3a724443653e0fb51fdf94835d2b9694a4188507 Mon Sep 17 00:00:00 2001 From: Kota-Jagadeesh Date: Mon, 20 Apr 2026 22:17:28 +0530 Subject: [PATCH 7/9] test: update thanks tests for author-aware and fallback message cases --- .../kotlin/fr/free/nrw/commons/review/ReviewControllerTest.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewControllerTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewControllerTest.kt index 40eaaef664..799836605e 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewControllerTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewControllerTest.kt @@ -179,9 +179,8 @@ class ReviewControllerTest { assertEquals( ShadowToast.getTextOfLatestToast().toString(), context.getString( - R.string.send_thank_toast, + R.string.send_thank_toast_no_author, media.displayTitle, - context.getString(R.string.unknown) ), ) } From 4558ed829620c93a750d95f10c7c8b158d7a38c5 Mon Sep 17 00:00:00 2001 From: Kota-Jagadeesh Date: Mon, 20 Apr 2026 22:20:17 +0530 Subject: [PATCH 8/9] feat: include author name in thanks messages with null-safe handling --- .../nrw/commons/review/ReviewController.kt | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewController.kt b/app/src/main/java/fr/free/nrw/commons/review/ReviewController.kt index 78c4007e98..ade1d4dff2 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewController.kt +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewController.kt @@ -165,11 +165,11 @@ class ReviewController @Inject constructor( .commonsApplicationComponent .inject(this) - val authorName = firstRevision?.user() ?: context.getString(R.string.unknown) - ViewUtil.showShortToast( - context, - context.getString(R.string.send_thank_toast, media?.displayTitle ?: "", authorName) - ) + val authorName = firstRevision?.user() + val message = if (authorName != null) { + context.getString(R.string.send_thank_toast, media?.displayTitle, authorName) + } else { context.getString(R.string.send_thank_toast_no_author, media?.displayTitle) } + ViewUtil.showShortToast(context, message) if (firstRevision == null) return @@ -197,13 +197,17 @@ class ReviewController @Inject constructor( @SuppressLint("StringFormatInvalid") private fun displayThanksToast(context: Context, result: Boolean) { - val authorName = firstRevision?.user() ?: context.getString(R.string.unknown) + val authorName = firstRevision?.user() val (title, message) = if (result) { context.getString(R.string.send_thank_success_title) to - context.getString(R.string.send_thank_success_message, media?.displayTitle, authorName) + if (authorName != null) { + context.getString(R.string.send_thank_success_message, media?.displayTitle, authorName) + } else { context.getString(R.string.send_thank_success_message_no_author, media?.displayTitle) } } else { context.getString(R.string.send_thank_failure_title) to - context.getString(R.string.send_thank_failure_message, media?.displayTitle, authorName) + if (authorName != null) { + context.getString(R.string.send_thank_failure_message, media?.displayTitle, authorName) + } else { context.getString(R.string.send_thank_failure_message_no_author, media?.displayTitle) } } ViewUtil.showShortToast(context, message) From 7a64c4274d9d875eb09018ac9487f3ab7c93f402 Mon Sep 17 00:00:00 2001 From: Kota-Jagadeesh Date: Thu, 23 Apr 2026 08:59:03 +0530 Subject: [PATCH 9/9] refactor: use kotlin null handling for author in thanks messages --- .../nrw/commons/review/ReviewController.kt | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewController.kt b/app/src/main/java/fr/free/nrw/commons/review/ReviewController.kt index ade1d4dff2..f34e09f4ab 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewController.kt +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewController.kt @@ -165,10 +165,11 @@ class ReviewController @Inject constructor( .commonsApplicationComponent .inject(this) - val authorName = firstRevision?.user() - val message = if (authorName != null) { - context.getString(R.string.send_thank_toast, media?.displayTitle, authorName) - } else { context.getString(R.string.send_thank_toast_no_author, media?.displayTitle) } + val message = firstRevision?.user()?.let { + context.getString(R.string.send_thank_toast, media?.displayTitle, it) + } ?: run { + context.getString(R.string.send_thank_toast_no_author, media?.displayTitle) + } ViewUtil.showShortToast(context, message) if (firstRevision == null) return @@ -197,17 +198,20 @@ class ReviewController @Inject constructor( @SuppressLint("StringFormatInvalid") private fun displayThanksToast(context: Context, result: Boolean) { - val authorName = firstRevision?.user() val (title, message) = if (result) { context.getString(R.string.send_thank_success_title) to - if (authorName != null) { - context.getString(R.string.send_thank_success_message, media?.displayTitle, authorName) - } else { context.getString(R.string.send_thank_success_message_no_author, media?.displayTitle) } + (firstRevision?.user()?.let { + context.getString(R.string.send_thank_success_message, media?.displayTitle, it) + } ?: run { + context.getString(R.string.send_thank_success_message_no_author, media?.displayTitle) + }) } else { context.getString(R.string.send_thank_failure_title) to - if (authorName != null) { - context.getString(R.string.send_thank_failure_message, media?.displayTitle, authorName) - } else { context.getString(R.string.send_thank_failure_message_no_author, media?.displayTitle) } + (firstRevision?.user()?.let { + context.getString(R.string.send_thank_failure_message, media?.displayTitle, it) + } ?: run { + context.getString(R.string.send_thank_failure_message_no_author, media?.displayTitle) + }) } ViewUtil.showShortToast(context, message)