Add copy tags to clipboard feature (#9063)
Cette révision appartient à :
Parent
4d607c4aed
révision
d02b0ca2db
4 fichiers modifiés avec 65 ajouts et 11 suppressions
|
@ -72,6 +72,7 @@ import eu.kanade.tachiyomi.ui.manga.ChapterItem
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaScreenState
|
import eu.kanade.tachiyomi.ui.manga.MangaScreenState
|
||||||
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 tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
|
@ -91,7 +92,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,
|
||||||
|
@ -117,6 +121,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,
|
||||||
|
@ -130,7 +141,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,
|
||||||
|
@ -161,7 +173,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,
|
||||||
|
@ -195,7 +208,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,
|
||||||
|
@ -363,7 +380,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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,7 +424,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,
|
||||||
|
@ -555,7 +577,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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -35,6 +35,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.LocalMinimumTouchTargetEnforcement
|
import androidx.compose.material3.LocalMinimumTouchTargetEnforcement
|
||||||
|
@ -72,6 +73,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
|
||||||
|
@ -210,7 +212,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,
|
||||||
) {
|
) {
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
val (expanded, onExpanded) = rememberSaveable {
|
val (expanded, onExpanded) = rememberSaveable {
|
||||||
|
@ -240,6 +243,27 @@ 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
|
||||||
|
},
|
||||||
|
)
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text(text = stringResource(R.string.action_copy_to_clipboard)) },
|
||||||
|
onClick = {
|
||||||
|
onCopyTagToClipboard(tagSelected)
|
||||||
|
showMenu = false
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
if (expanded) {
|
if (expanded) {
|
||||||
FlowRow(
|
FlowRow(
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
@ -249,7 +273,10 @@ fun ExpandableMangaDescription(
|
||||||
tags.forEach {
|
tags.forEach {
|
||||||
TagsChip(
|
TagsChip(
|
||||||
text = it,
|
text = it,
|
||||||
onClick = { onTagClicked(it) },
|
onClick = {
|
||||||
|
tagSelected = it
|
||||||
|
showMenu = true
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -261,7 +288,10 @@ fun ExpandableMangaDescription(
|
||||||
items(items = tags) {
|
items(items = tags) {
|
||||||
TagsChip(
|
TagsChip(
|
||||||
text = it,
|
text = it,
|
||||||
onClick = { onTagClicked(it) },
|
onClick = {
|
||||||
|
tagSelected = it
|
||||||
|
showMenu = true
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,7 +114,7 @@ class MangaScreen(
|
||||||
onWebViewClicked = { openMangaInWebView(navigator, screenModel.manga, screenModel.source) }.takeIf { isHttpSource },
|
onWebViewClicked = { openMangaInWebView(navigator, screenModel.manga, screenModel.source) }.takeIf { isHttpSource },
|
||||||
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>
|
||||||
|
|
Référencer dans un nouveau ticket