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:
parent
829452bc03
commit
57113014ec
@ -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) {
|
||||||
|
@ -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 <--
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 = {},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,7 +225,7 @@ open class BrowseSourceScreenModel(
|
|||||||
.combineMetadata(dbManga, metadata)
|
.combineMetadata(dbManga, metadata)
|
||||||
// SY <--
|
// SY <--
|
||||||
.stateIn(coroutineScope)
|
.stateIn(coroutineScope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.cachedIn(coroutineScope)
|
.cachedIn(coroutineScope)
|
||||||
}
|
}
|
||||||
|
@ -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()) },
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user