Add copy tags to clipboard feature (#9063)

(cherry picked from commit d02b0ca2db744b0eca48dfca86811d653fd3bdaf)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt
#	app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt
This commit is contained in:
0x7673 2023-02-14 09:22:10 +05:30 committed by Jobobby04
parent 829452bc03
commit 57113014ec
6 changed files with 80 additions and 24 deletions

View File

@ -85,6 +85,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.ui.manga.PagePreviewState import eu.kanade.tachiyomi.ui.manga.PagePreviewState
import eu.kanade.tachiyomi.ui.manga.chapterDecimalFormat import eu.kanade.tachiyomi.ui.manga.chapterDecimalFormat
import eu.kanade.tachiyomi.util.lang.toRelativeString import eu.kanade.tachiyomi.util.lang.toRelativeString
import eu.kanade.tachiyomi.util.system.copyToClipboard
import exh.metadata.MetadataUtil import exh.metadata.MetadataUtil
import exh.source.MERGED_SOURCE_ID import exh.source.MERGED_SOURCE_ID
import exh.source.getMainSource import exh.source.getMainSource
@ -115,7 +116,10 @@ fun MangaScreen(
onWebViewClicked: (() -> Unit)?, onWebViewClicked: (() -> Unit)?,
onWebViewLongClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?,
onTrackingClicked: (() -> Unit)?, onTrackingClicked: (() -> Unit)?,
onTagClicked: (String) -> Unit,
// For tags menu
onTagSearch: (String) -> Unit,
onFilterButtonClicked: () -> Unit, onFilterButtonClicked: () -> Unit,
onRefresh: () -> Unit, onRefresh: () -> Unit,
onContinueReading: () -> Unit, onContinueReading: () -> Unit,
@ -151,6 +155,13 @@ fun MangaScreen(
onAllChapterSelected: (Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit, onInvertSelection: () -> Unit,
) { ) {
val context = LocalContext.current
val onCopyTagToClipboard: (tag: String) -> Unit = {
if (it.isNotEmpty()) {
context.copyToClipboard(it, it)
}
}
if (!isTabletUi) { if (!isTabletUi) {
MangaScreenSmallImpl( MangaScreenSmallImpl(
state = state, state = state,
@ -164,7 +175,8 @@ fun MangaScreen(
onWebViewClicked = onWebViewClicked, onWebViewClicked = onWebViewClicked,
onWebViewLongClicked = onWebViewLongClicked, onWebViewLongClicked = onWebViewLongClicked,
onTrackingClicked = onTrackingClicked, onTrackingClicked = onTrackingClicked,
onTagClicked = onTagClicked, onTagSearch = onTagSearch,
onCopyTagToClipboard = onCopyTagToClipboard,
onFilterClicked = onFilterButtonClicked, onFilterClicked = onFilterButtonClicked,
onRefresh = onRefresh, onRefresh = onRefresh,
onContinueReading = onContinueReading, onContinueReading = onContinueReading,
@ -205,7 +217,8 @@ fun MangaScreen(
onWebViewClicked = onWebViewClicked, onWebViewClicked = onWebViewClicked,
onWebViewLongClicked = onWebViewLongClicked, onWebViewLongClicked = onWebViewLongClicked,
onTrackingClicked = onTrackingClicked, onTrackingClicked = onTrackingClicked,
onTagClicked = onTagClicked, onTagSearch = onTagSearch,
onCopyTagToClipboard = onCopyTagToClipboard,
onFilterButtonClicked = onFilterButtonClicked, onFilterButtonClicked = onFilterButtonClicked,
onRefresh = onRefresh, onRefresh = onRefresh,
onContinueReading = onContinueReading, onContinueReading = onContinueReading,
@ -249,7 +262,11 @@ private fun MangaScreenSmallImpl(
onWebViewClicked: (() -> Unit)?, onWebViewClicked: (() -> Unit)?,
onWebViewLongClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?,
onTrackingClicked: (() -> Unit)?, onTrackingClicked: (() -> Unit)?,
onTagClicked: (String) -> Unit,
// For tags menu
onTagSearch: (String) -> Unit,
onCopyTagToClipboard: (tag: String) -> Unit,
onFilterClicked: () -> Unit, onFilterClicked: () -> Unit,
onRefresh: () -> Unit, onRefresh: () -> Unit,
onContinueReading: () -> Unit, onContinueReading: () -> Unit,
@ -454,7 +471,8 @@ private fun MangaScreenSmallImpl(
defaultExpandState = state.isFromSource, defaultExpandState = state.isFromSource,
description = state.manga.description, description = state.manga.description,
tagsProvider = { state.manga.genre }, tagsProvider = { state.manga.genre },
onTagClicked = onTagClicked, onTagSearch = onTagSearch,
onCopyTagToClipboard = onCopyTagToClipboard,
// SY --> // SY -->
doSearch = onSearch, doSearch = onSearch,
searchMetadataChips = remember(state.meta, state.source.id, state.manga.genre) { searchMetadataChips = remember(state.meta, state.source.id, state.manga.genre) {
@ -535,7 +553,11 @@ fun MangaScreenLargeImpl(
onWebViewClicked: (() -> Unit)?, onWebViewClicked: (() -> Unit)?,
onWebViewLongClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?,
onTrackingClicked: (() -> Unit)?, onTrackingClicked: (() -> Unit)?,
onTagClicked: (String) -> Unit,
// For tags menu
onTagSearch: (String) -> Unit,
onCopyTagToClipboard: (tag: String) -> Unit,
onFilterButtonClicked: () -> Unit, onFilterButtonClicked: () -> Unit,
onRefresh: () -> Unit, onRefresh: () -> Unit,
onContinueReading: () -> Unit, onContinueReading: () -> Unit,
@ -714,7 +736,8 @@ fun MangaScreenLargeImpl(
defaultExpandState = true, defaultExpandState = true,
description = state.manga.description, description = state.manga.description,
tagsProvider = { state.manga.genre }, tagsProvider = { state.manga.genre },
onTagClicked = onTagClicked, onTagSearch = onTagSearch,
onCopyTagToClipboard = onCopyTagToClipboard,
// SY --> // SY -->
doSearch = onSearch, doSearch = onSearch,
searchMetadataChips = remember(state.meta, state.source.id, state.manga.genre) { searchMetadataChips = remember(state.meta, state.source.id, state.manga.genre) {

View File

@ -36,6 +36,7 @@ import androidx.compose.material.icons.outlined.Pause
import androidx.compose.material.icons.outlined.Public import androidx.compose.material.icons.outlined.Public
import androidx.compose.material.icons.outlined.Schedule import androidx.compose.material.icons.outlined.Schedule
import androidx.compose.material.icons.outlined.Sync import androidx.compose.material.icons.outlined.Sync
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -70,6 +71,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.google.accompanist.flowlayout.FlowRow import com.google.accompanist.flowlayout.FlowRow
import eu.kanade.presentation.components.DropdownMenu
import eu.kanade.presentation.components.MangaCover import eu.kanade.presentation.components.MangaCover
import eu.kanade.presentation.components.TextButton import eu.kanade.presentation.components.TextButton
import eu.kanade.presentation.util.clickableNoIndication import eu.kanade.presentation.util.clickableNoIndication
@ -221,7 +223,8 @@ fun ExpandableMangaDescription(
defaultExpandState: Boolean, defaultExpandState: Boolean,
description: String?, description: String?,
tagsProvider: () -> List<String>?, tagsProvider: () -> List<String>?,
onTagClicked: (String) -> Unit, onTagSearch: (String) -> Unit,
onCopyTagToClipboard: (tag: String) -> Unit,
// SY --> // SY -->
searchMetadataChips: SearchMetadataChips?, searchMetadataChips: SearchMetadataChips?,
doSearch: (query: String, global: Boolean) -> Unit, doSearch: (query: String, global: Boolean) -> Unit,
@ -255,13 +258,45 @@ fun ExpandableMangaDescription(
.padding(vertical = 12.dp) .padding(vertical = 12.dp)
.animateContentSize(), .animateContentSize(),
) { ) {
var showMenu by remember { mutableStateOf(false) }
var tagSelected by remember { mutableStateOf("") }
DropdownMenu(
expanded = showMenu,
onDismissRequest = { showMenu = false },
) {
DropdownMenuItem(
text = { Text(text = stringResource(R.string.action_search)) },
onClick = {
onTagSearch(tagSelected)
showMenu = false
},
)
// SY -->
DropdownMenuItem(
text = { Text(text = stringResource(R.string.action_global_search)) },
onClick = {
doSearch(tagSelected, true)
showMenu = false
},
)
// SY <--
DropdownMenuItem(
text = { Text(text = stringResource(R.string.action_copy_to_clipboard)) },
onClick = {
onCopyTagToClipboard(tagSelected)
showMenu = false
},
)
}
if (expanded) { if (expanded) {
// SY --> // SY -->
if (searchMetadataChips != null) { if (searchMetadataChips != null) {
NamespaceTags( NamespaceTags(
tags = searchMetadataChips, tags = searchMetadataChips,
onClick = onTagClicked, onClick = {
onLongClick = { doSearch(it, true) }, tagSelected = it
showMenu = true
},
) )
} else { } else {
// SY <-- // SY <--
@ -273,10 +308,10 @@ fun ExpandableMangaDescription(
tags.forEach { tags.forEach {
TagsChip( TagsChip(
text = it, text = it,
onClick = { onTagClicked(it) }, onClick = {
// SY --> tagSelected = it
onLongClick = { doSearch(it, true) }, showMenu = true
// SY <-- },
) )
} }
} }
@ -289,10 +324,10 @@ fun ExpandableMangaDescription(
items(items = tags) { items(items = tags) {
TagsChip( TagsChip(
text = it, text = it,
onClick = { onTagClicked(it) }, onClick = {
// SY --> tagSelected = it
onLongClick = { doSearch(it, true) }, showMenu = true
// SY <-- },
) )
} }
} }

View File

@ -98,7 +98,6 @@ value class SearchMetadataChips(
fun NamespaceTags( fun NamespaceTags(
tags: SearchMetadataChips, tags: SearchMetadataChips,
onClick: (item: String) -> Unit, onClick: (item: String) -> Unit,
onLongClick: (item: String) -> Unit,
) { ) {
Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(4.dp)) { Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(4.dp)) {
tags.tags.forEach { (namespace, tags) -> tags.tags.forEach { (namespace, tags) ->
@ -116,7 +115,6 @@ fun NamespaceTags(
TagsChip( TagsChip(
text = text, text = text,
onClick = { onClick(search) }, onClick = { onClick(search) },
onLongClick = { onLongClick(search) },
border = borderDp?.let { border = borderDp?.let {
SuggestionChipDefaults.suggestionChipBorder(borderWidth = it) SuggestionChipDefaults.suggestionChipBorder(borderWidth = it)
}, },
@ -135,7 +133,7 @@ fun NamespaceTags(
fun TagsChip( fun TagsChip(
text: String, text: String,
onClick: (() -> Unit)?, onClick: (() -> Unit)?,
onLongClick: (() -> Unit)?, onLongClick: (() -> Unit)? = null,
border: ChipBorder? = null, border: ChipBorder? = null,
borderM3: ChipBorderM3? = null, borderM3: ChipBorderM3? = null,
) { ) {
@ -243,7 +241,6 @@ fun NamespaceTagsPreview() {
}.let { SearchMetadataChips(it, EHentai(EXH_SOURCE_ID, true, context), emptyList()) }!! }.let { SearchMetadataChips(it, EHentai(EXH_SOURCE_ID, true, context), emptyList()) }!!
}, },
onClick = {}, onClick = {},
onLongClick = {},
) )
} }
} }

View File

@ -225,7 +225,7 @@ open class BrowseSourceScreenModel(
.combineMetadata(dbManga, metadata) .combineMetadata(dbManga, metadata)
// SY <-- // SY <--
.stateIn(coroutineScope) .stateIn(coroutineScope)
} }
} }
.cachedIn(coroutineScope) .cachedIn(coroutineScope)
} }

View File

@ -161,7 +161,7 @@ class MangaScreen(
// SY <-- // SY <--
onWebViewLongClicked = { copyMangaUrl(context, screenModel.manga, screenModel.source) }.takeIf { isHttpSource }, onWebViewLongClicked = { copyMangaUrl(context, screenModel.manga, screenModel.source) }.takeIf { isHttpSource },
onTrackingClicked = screenModel::showTrackDialog.takeIf { successState.trackingAvailable }, onTrackingClicked = screenModel::showTrackDialog.takeIf { successState.trackingAvailable },
onTagClicked = { scope.launch { performGenreSearch(navigator, it, screenModel.source!!) } }, onTagSearch = { scope.launch { performGenreSearch(navigator, it, screenModel.source!!) } },
onFilterButtonClicked = screenModel::showSettingsDialog, onFilterButtonClicked = screenModel::showSettingsDialog,
onRefresh = screenModel::fetchAllFromSource, onRefresh = screenModel::fetchAllFromSource,
onContinueReading = { continueReading(context, screenModel.getNextUnreadChapter()) }, onContinueReading = { continueReading(context, screenModel.getNextUnreadChapter()) },

View File

@ -96,6 +96,7 @@
<string name="action_resume">Resume</string> <string name="action_resume">Resume</string>
<string name="action_open_in_browser">Open in browser</string> <string name="action_open_in_browser">Open in browser</string>
<string name="action_show_manga">Show entry</string> <string name="action_show_manga">Show entry</string>
<string name="action_copy_to_clipboard">Copy to clipboard</string>
<!-- Do not translate "WebView" --> <!-- Do not translate "WebView" -->
<string name="action_open_in_web_view">Open in WebView</string> <string name="action_open_in_web_view">Open in WebView</string>
<string name="action_web_view" translatable="false">WebView</string> <string name="action_web_view" translatable="false">WebView</string>