Make "File usages" items clickable with correct URLs #6307 (#6405)
Some checks failed
Android CI / Run tests and generate APK (push) Has been cancelled

* Fix: URL generation for GlobalFileUsage in FileUsagesUiModel.kt for issue #6307

* Add clickable functionality to 'Usages on Other Wikis' in FileUsagesContainer for issue #6307

---------

Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com>
This commit is contained in:
VoidRaven 2025-09-02 13:33:28 +05:30 committed by GitHub
parent dd96c64182
commit e2c8f85a5b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 47 additions and 31 deletions

View file

@ -1,7 +1,10 @@
package fr.free.nrw.commons.fileusages package fr.free.nrw.commons.fileusages
import android.net.Uri
import timber.log.Timber
/** /**
* Show where file is being used on Commons and oher wikis. * shows where file is being used on Commons and other wikis.
*/ */
data class FileUsagesUiModel( data class FileUsagesUiModel(
val title: String, val title: String,
@ -9,10 +12,37 @@ data class FileUsagesUiModel(
) )
fun FileUsage.toUiModel(): FileUsagesUiModel { fun FileUsage.toUiModel(): FileUsagesUiModel {
return FileUsagesUiModel(title = title, link = "https://commons.wikimedia.org/wiki/$title") return FileUsagesUiModel(
title = title,
link = "https://commons.wikimedia.org/wiki/${Uri.encode(title.replace(" ", "_"))}"
)
} }
fun GlobalFileUsage.toUiModel(): FileUsagesUiModel { fun GlobalFileUsage.toUiModel(): FileUsagesUiModel {
// link is associated with sub items under wiki group (which is not used ATM) Timber.d("GlobalFileUsage: wiki=%s, title=%s", wiki, title)
return FileUsagesUiModel(title = wiki, link = null)
// handles the empty or invalid wiki/title
if (wiki.isEmpty() || title.isEmpty()) {
Timber.w("Invalid GlobalFileUsage: wiki=%s, title=%s", wiki, title)
return FileUsagesUiModel(title = title, link = null)
}
// determines the domain
val domain = when {
wiki.contains(".") -> wiki // Already a full domain like "en.wikipedia.org"
wiki == "commonswiki" -> "commons.wikimedia.org"
wiki.endsWith("wiki") -> {
val code = wiki.removeSuffix("wiki")
"$code.wikipedia.org"
}
else -> "$wiki.wikipedia.org" // fallback for codes like "en"
}
val normalizedTitle = Uri.encode(title.replace(" ", "_"))
// construct full URL
val url = "https://$domain/wiki/$normalizedTitle"
Timber.d("Generated URL for GlobalFileUsage: %s", url)
return FileUsagesUiModel(title = title, link = url)
} }

View file

@ -2128,22 +2128,17 @@ fun FileUsagesContainer(
val uriHandle = LocalUriHandler.current val uriHandle = LocalUriHandler.current
Column(modifier = modifier) { Column(modifier = modifier) {
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween horizontalArrangement = Arrangement.SpaceBetween
) { ) {
Text( Text(
text = stringResource(R.string.usages_on_commons_heading), text = stringResource(R.string.usages_on_commons_heading),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
style = MaterialTheme.typography.titleSmall style = MaterialTheme.typography.titleSmall
) )
IconButton(onClick = { isCommonsListExpanded = !isCommonsListExpanded }) {
IconButton(onClick = {
isCommonsListExpanded = !isCommonsListExpanded
}) {
Icon( Icon(
imageVector = if (isCommonsListExpanded) Icons.Default.KeyboardArrowUp imageVector = if (isCommonsListExpanded) Icons.Default.KeyboardArrowUp
else Icons.Default.KeyboardArrowDown, else Icons.Default.KeyboardArrowDown,
@ -2157,11 +2152,8 @@ fun FileUsagesContainer(
MediaDetailViewModel.FileUsagesContainerState.Loading -> { MediaDetailViewModel.FileUsagesContainerState.Loading -> {
LinearProgressIndicator() LinearProgressIndicator()
} }
is MediaDetailViewModel.FileUsagesContainerState.Success -> { is MediaDetailViewModel.FileUsagesContainerState.Success -> {
val data = commonsContainerState.data val data = commonsContainerState.data
if (data.isNullOrEmpty()) { if (data.isNullOrEmpty()) {
ListItem(headlineContent = { ListItem(headlineContent = {
Text( Text(
@ -2181,7 +2173,7 @@ fun FileUsagesContainer(
headlineContent = { headlineContent = {
Text( Text(
modifier = Modifier.clickable { modifier = Modifier.clickable {
uriHandle.openUri(usage.link!!) usage.link?.let { uriHandle.openUri(it) }
}, },
text = usage.title, text = usage.title,
style = MaterialTheme.typography.titleSmall.copy( style = MaterialTheme.typography.titleSmall.copy(
@ -2189,11 +2181,11 @@ fun FileUsagesContainer(
textDecoration = TextDecoration.Underline textDecoration = TextDecoration.Underline
) )
) )
}) }
)
} }
} }
} }
is MediaDetailViewModel.FileUsagesContainerState.Error -> { is MediaDetailViewModel.FileUsagesContainerState.Error -> {
ListItem(headlineContent = { ListItem(headlineContent = {
Text( Text(
@ -2203,12 +2195,10 @@ fun FileUsagesContainer(
) )
}) })
} }
MediaDetailViewModel.FileUsagesContainerState.Initial -> {} MediaDetailViewModel.FileUsagesContainerState.Initial -> {}
} }
} }
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@ -2219,10 +2209,7 @@ fun FileUsagesContainer(
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
style = MaterialTheme.typography.titleSmall style = MaterialTheme.typography.titleSmall
) )
IconButton(onClick = { isOtherWikisListExpanded = !isOtherWikisListExpanded }) {
IconButton(onClick = {
isOtherWikisListExpanded = !isOtherWikisListExpanded
}) {
Icon( Icon(
imageVector = if (isOtherWikisListExpanded) Icons.Default.KeyboardArrowUp imageVector = if (isOtherWikisListExpanded) Icons.Default.KeyboardArrowUp
else Icons.Default.KeyboardArrowDown, else Icons.Default.KeyboardArrowDown,
@ -2236,11 +2223,8 @@ fun FileUsagesContainer(
MediaDetailViewModel.FileUsagesContainerState.Loading -> { MediaDetailViewModel.FileUsagesContainerState.Loading -> {
LinearProgressIndicator() LinearProgressIndicator()
} }
is MediaDetailViewModel.FileUsagesContainerState.Success -> { is MediaDetailViewModel.FileUsagesContainerState.Success -> {
val data = globalContainerState.data val data = globalContainerState.data
if (data.isNullOrEmpty()) { if (data.isNullOrEmpty()) {
ListItem(headlineContent = { ListItem(headlineContent = {
Text( Text(
@ -2259,16 +2243,20 @@ fun FileUsagesContainer(
}, },
headlineContent = { headlineContent = {
Text( Text(
modifier = Modifier.clickable {
usage.link?.let { uriHandle.openUri(it) }
},
text = usage.title, text = usage.title,
style = MaterialTheme.typography.titleSmall.copy( style = MaterialTheme.typography.titleSmall.copy(
color = Color(0xFF5A6AEC),
textDecoration = TextDecoration.Underline textDecoration = TextDecoration.Underline
) )
) )
}) }
)
} }
} }
} }
is MediaDetailViewModel.FileUsagesContainerState.Error -> { is MediaDetailViewModel.FileUsagesContainerState.Error -> {
ListItem(headlineContent = { ListItem(headlineContent = {
Text( Text(
@ -2278,10 +2266,8 @@ fun FileUsagesContainer(
) )
}) })
} }
MediaDetailViewModel.FileUsagesContainerState.Initial -> {} MediaDetailViewModel.FileUsagesContainerState.Initial -> {}
} }
} }
} }
} }