123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- package eu.kanade.presentation.track
- import androidx.annotation.StringRes
- import androidx.compose.animation.animateContentSize
- import androidx.compose.foundation.background
- import androidx.compose.foundation.clickable
- import androidx.compose.foundation.combinedClickable
- import androidx.compose.foundation.layout.Arrangement
- import androidx.compose.foundation.layout.Box
- import androidx.compose.foundation.layout.Column
- import androidx.compose.foundation.layout.IntrinsicSize
- import androidx.compose.foundation.layout.Row
- import androidx.compose.foundation.layout.WindowInsets
- import androidx.compose.foundation.layout.fillMaxHeight
- import androidx.compose.foundation.layout.fillMaxWidth
- import androidx.compose.foundation.layout.height
- import androidx.compose.foundation.layout.padding
- import androidx.compose.foundation.layout.systemBars
- import androidx.compose.foundation.layout.windowInsetsPadding
- import androidx.compose.foundation.layout.wrapContentSize
- import androidx.compose.foundation.rememberScrollState
- import androidx.compose.foundation.shape.RoundedCornerShape
- import androidx.compose.foundation.verticalScroll
- import androidx.compose.material.icons.Icons
- import androidx.compose.material.icons.filled.MoreVert
- import androidx.compose.material3.DropdownMenuItem
- import androidx.compose.material3.HorizontalDivider
- import androidx.compose.material3.Icon
- import androidx.compose.material3.IconButton
- import androidx.compose.material3.MaterialTheme
- import androidx.compose.material3.Surface
- import androidx.compose.material3.Text
- import androidx.compose.material3.TextButton
- import androidx.compose.material3.VerticalDivider
- import androidx.compose.runtime.Composable
- import androidx.compose.runtime.getValue
- import androidx.compose.runtime.mutableStateOf
- import androidx.compose.runtime.remember
- import androidx.compose.runtime.setValue
- import androidx.compose.ui.Alignment
- import androidx.compose.ui.Modifier
- import androidx.compose.ui.draw.alpha
- import androidx.compose.ui.draw.clip
- import androidx.compose.ui.platform.LocalContext
- import androidx.compose.ui.res.stringResource
- import androidx.compose.ui.text.style.TextAlign
- import androidx.compose.ui.text.style.TextOverflow
- import androidx.compose.ui.tooling.preview.PreviewLightDark
- import androidx.compose.ui.tooling.preview.PreviewParameter
- import androidx.compose.ui.unit.dp
- import eu.kanade.domain.track.model.toDbTrack
- import eu.kanade.presentation.components.DropdownMenu
- import eu.kanade.presentation.theme.TachiyomiTheme
- import eu.kanade.presentation.track.components.TrackLogoIcon
- import eu.kanade.tachiyomi.R
- import eu.kanade.tachiyomi.data.track.Tracker
- import eu.kanade.tachiyomi.ui.manga.track.TrackItem
- import eu.kanade.tachiyomi.util.system.copyToClipboard
- import java.text.DateFormat
- private const val UnsetStatusTextAlpha = 0.5F
- @Composable
- fun TrackInfoDialogHome(
- trackItems: List<TrackItem>,
- dateFormat: DateFormat,
- onStatusClick: (TrackItem) -> Unit,
- onChapterClick: (TrackItem) -> Unit,
- onScoreClick: (TrackItem) -> Unit,
- onStartDateEdit: (TrackItem) -> Unit,
- onEndDateEdit: (TrackItem) -> Unit,
- onNewSearch: (TrackItem) -> Unit,
- onOpenInBrowser: (TrackItem) -> Unit,
- onRemoved: (TrackItem) -> Unit,
- ) {
- Column(
- modifier = Modifier
- .animateContentSize()
- .fillMaxWidth()
- .verticalScroll(rememberScrollState())
- .padding(16.dp)
- .windowInsetsPadding(WindowInsets.systemBars),
- verticalArrangement = Arrangement.spacedBy(24.dp),
- ) {
- trackItems.forEach { item ->
- if (item.track != null) {
- val supportsScoring = item.tracker.getScoreList().isNotEmpty()
- val supportsReadingDates = item.tracker.supportsReadingDates
- TrackInfoItem(
- title = item.track.title,
- tracker = item.tracker,
- status = item.tracker.getStatus(item.track.status.toInt()),
- onStatusClick = { onStatusClick(item) },
- chapters = "${item.track.lastChapterRead.toInt()}".let {
- val totalChapters = item.track.totalChapters
- if (totalChapters > 0) {
- // Add known total chapter count
- "$it / $totalChapters"
- } else {
- it
- }
- },
- onChaptersClick = { onChapterClick(item) },
- score = item.tracker.displayScore(item.track.toDbTrack())
- .takeIf { supportsScoring && item.track.score != 0.0 },
- onScoreClick = { onScoreClick(item) }
- .takeIf { supportsScoring },
- startDate = remember(item.track.startDate) { dateFormat.format(item.track.startDate) }
- .takeIf { supportsReadingDates && item.track.startDate != 0L },
- onStartDateClick = { onStartDateEdit(item) } // TODO
- .takeIf { supportsReadingDates },
- endDate = dateFormat.format(item.track.finishDate)
- .takeIf { supportsReadingDates && item.track.finishDate != 0L },
- onEndDateClick = { onEndDateEdit(item) }
- .takeIf { supportsReadingDates },
- onNewSearch = { onNewSearch(item) },
- onOpenInBrowser = { onOpenInBrowser(item) },
- onRemoved = { onRemoved(item) },
- )
- } else {
- TrackInfoItemEmpty(
- tracker = item.tracker,
- onNewSearch = { onNewSearch(item) },
- )
- }
- }
- }
- }
- @Composable
- private fun TrackInfoItem(
- title: String,
- tracker: Tracker,
- @StringRes status: Int?,
- onStatusClick: () -> Unit,
- chapters: String,
- onChaptersClick: () -> Unit,
- score: String?,
- onScoreClick: (() -> Unit)?,
- startDate: String?,
- onStartDateClick: (() -> Unit)?,
- endDate: String?,
- onEndDateClick: (() -> Unit)?,
- onNewSearch: () -> Unit,
- onOpenInBrowser: () -> Unit,
- onRemoved: () -> Unit,
- ) {
- val context = LocalContext.current
- Column {
- Row(
- verticalAlignment = Alignment.CenterVertically,
- ) {
- TrackLogoIcon(
- tracker = tracker,
- onClick = onOpenInBrowser,
- )
- Box(
- modifier = Modifier
- .height(48.dp)
- .weight(1f)
- .combinedClickable(
- onClick = onNewSearch,
- onLongClick = {
- context.copyToClipboard(title, title)
- },
- )
- .padding(start = 16.dp),
- contentAlignment = Alignment.CenterStart,
- ) {
- Text(
- text = title,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- style = MaterialTheme.typography.titleMedium,
- color = MaterialTheme.colorScheme.onSurface,
- )
- }
- VerticalDivider()
- TrackInfoItemMenu(
- onOpenInBrowser = onOpenInBrowser,
- onRemoved = onRemoved,
- )
- }
- Box(
- modifier = Modifier
- .padding(top = 12.dp)
- .clip(MaterialTheme.shapes.medium)
- .background(MaterialTheme.colorScheme.surface)
- .padding(8.dp)
- .clip(RoundedCornerShape(6.dp)),
- ) {
- Column {
- Row(modifier = Modifier.height(IntrinsicSize.Min)) {
- TrackDetailsItem(
- modifier = Modifier.weight(1f),
- text = status?.let { stringResource(it) } ?: "",
- onClick = onStatusClick,
- )
- VerticalDivider()
- TrackDetailsItem(
- modifier = Modifier.weight(1f),
- text = chapters,
- onClick = onChaptersClick,
- )
- if (onScoreClick != null) {
- VerticalDivider()
- TrackDetailsItem(
- modifier = Modifier
- .weight(1f)
- .alpha(if (score == null) UnsetStatusTextAlpha else 1f),
- text = score ?: stringResource(R.string.score),
- onClick = onScoreClick,
- )
- }
- }
- if (onStartDateClick != null && onEndDateClick != null) {
- HorizontalDivider()
- Row(modifier = Modifier.height(IntrinsicSize.Min)) {
- TrackDetailsItem(
- modifier = Modifier.weight(1F),
- text = startDate,
- placeholder = stringResource(R.string.track_started_reading_date),
- onClick = onStartDateClick,
- )
- VerticalDivider()
- TrackDetailsItem(
- modifier = Modifier.weight(1F),
- text = endDate,
- placeholder = stringResource(R.string.track_finished_reading_date),
- onClick = onEndDateClick,
- )
- }
- }
- }
- }
- }
- }
- @Composable
- private fun TrackDetailsItem(
- text: String?,
- onClick: () -> Unit,
- modifier: Modifier = Modifier,
- placeholder: String = "",
- ) {
- Box(
- modifier = modifier
- .clickable(onClick = onClick)
- .alpha(if (text == null) UnsetStatusTextAlpha else 1f)
- .fillMaxHeight()
- .padding(12.dp),
- contentAlignment = Alignment.Center,
- ) {
- Text(
- text = text ?: placeholder,
- maxLines = 2,
- overflow = TextOverflow.Ellipsis,
- style = MaterialTheme.typography.bodyMedium,
- textAlign = TextAlign.Center,
- color = MaterialTheme.colorScheme.onSurface,
- )
- }
- }
- @Composable
- private fun TrackInfoItemEmpty(
- tracker: Tracker,
- onNewSearch: () -> Unit,
- ) {
- Row(
- verticalAlignment = Alignment.CenterVertically,
- ) {
- TrackLogoIcon(tracker)
- TextButton(
- onClick = onNewSearch,
- modifier = Modifier
- .padding(start = 16.dp)
- .weight(1f),
- ) {
- Text(text = stringResource(R.string.add_tracking))
- }
- }
- }
- @Composable
- private fun TrackInfoItemMenu(
- onOpenInBrowser: () -> Unit,
- onRemoved: () -> Unit,
- ) {
- var expanded by remember { mutableStateOf(false) }
- Box(modifier = Modifier.wrapContentSize(Alignment.TopStart)) {
- IconButton(onClick = { expanded = true }) {
- Icon(
- imageVector = Icons.Default.MoreVert,
- contentDescription = stringResource(R.string.label_more),
- )
- }
- DropdownMenu(
- expanded = expanded,
- onDismissRequest = { expanded = false },
- ) {
- DropdownMenuItem(
- text = { Text(stringResource(R.string.action_open_in_browser)) },
- onClick = {
- onOpenInBrowser()
- expanded = false
- },
- )
- DropdownMenuItem(
- text = { Text(stringResource(R.string.action_remove)) },
- onClick = {
- onRemoved()
- expanded = false
- },
- )
- }
- }
- }
- @PreviewLightDark
- @Composable
- private fun TrackInfoDialogHomePreviews(
- @PreviewParameter(TrackInfoDialogHomePreviewProvider::class)
- content: @Composable () -> Unit,
- ) {
- TachiyomiTheme {
- Surface {
- content()
- }
- }
- }
|