mirror of
https://github.com/aniyomiorg/aniyomi.git
synced 2024-11-26 23:18:17 +03:00
thank you android studio, very cool
This commit is contained in:
parent
f72df3f620
commit
2a875f6c86
2 changed files with 342 additions and 0 deletions
|
@ -0,0 +1,260 @@
|
|||
package eu.kanade.presentation.track.anime
|
||||
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.background
|
||||
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.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.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.VerticalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
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.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import eu.kanade.presentation.track.components.TrackLogoIcon
|
||||
import eu.kanade.presentation.track.manga.TrackDetailsItem
|
||||
import eu.kanade.presentation.track.manga.TrackInfoItemMenu
|
||||
import eu.kanade.tachiyomi.data.track.Tracker
|
||||
import eu.kanade.tachiyomi.ui.entries.anime.track.AnimeTrackItem
|
||||
import eu.kanade.tachiyomi.util.lang.toLocalDate
|
||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
private const val UnsetStatusTextAlpha = 0.5F
|
||||
|
||||
@Composable
|
||||
fun AnimeTrackInfoDialogHome(
|
||||
trackItems: List<AnimeTrackItem>,
|
||||
dateFormat: DateTimeFormatter,
|
||||
onStatusClick: (AnimeTrackItem) -> Unit,
|
||||
onEpisodeClick: (AnimeTrackItem) -> Unit,
|
||||
onScoreClick: (AnimeTrackItem) -> Unit,
|
||||
onStartDateEdit: (AnimeTrackItem) -> Unit,
|
||||
onEndDateEdit: (AnimeTrackItem) -> Unit,
|
||||
onNewSearch: (AnimeTrackItem) -> Unit,
|
||||
onOpenInBrowser: (AnimeTrackItem) -> Unit,
|
||||
onRemoved: (AnimeTrackItem) -> 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.animeService.getScoreList().isNotEmpty()
|
||||
val supportsReadingDates = item.tracker.supportsReadingDates
|
||||
TrackInfoItem(
|
||||
title = item.track.title,
|
||||
tracker = item.tracker,
|
||||
status = item.tracker.getStatus(item.track.status),
|
||||
onStatusClick = { onStatusClick(item) },
|
||||
episodes = "${item.track.lastEpisodeSeen.toInt()}".let {
|
||||
val totalEpisodes = item.track.totalEpisodes
|
||||
if (totalEpisodes > 0) {
|
||||
// Add known total episode count
|
||||
"$it / $totalEpisodes"
|
||||
} else {
|
||||
it
|
||||
}
|
||||
},
|
||||
onEpisodesClick = { onEpisodeClick(item) },
|
||||
score = item.tracker.animeService.displayScore(item.track)
|
||||
.takeIf { supportsScoring && item.track.score != 0.0 },
|
||||
onScoreClick = { onScoreClick(item) }
|
||||
.takeIf { supportsScoring },
|
||||
startDate = remember(item.track.startDate) {
|
||||
dateFormat.format(
|
||||
item.track.startDate.toLocalDate(),
|
||||
)
|
||||
}
|
||||
.takeIf { supportsReadingDates && item.track.startDate != 0L },
|
||||
onStartDateClick = { onStartDateEdit(item) } // TODO
|
||||
.takeIf { supportsReadingDates },
|
||||
endDate = dateFormat.format(item.track.finishDate.toLocalDate())
|
||||
.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,
|
||||
status: StringResource?,
|
||||
onStatusClick: () -> Unit,
|
||||
episodes: String,
|
||||
onEpisodesClick: () -> 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 = episodes,
|
||||
onClick = onEpisodesClick,
|
||||
)
|
||||
if (onScoreClick != null) {
|
||||
VerticalDivider()
|
||||
TrackDetailsItem(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.alpha(if (score == null) UnsetStatusTextAlpha else 1f),
|
||||
text = score ?: stringResource(MR.strings.score),
|
||||
onClick = onScoreClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (onStartDateClick != null && onEndDateClick != null) {
|
||||
HorizontalDivider()
|
||||
Row(modifier = Modifier.height(IntrinsicSize.Min)) {
|
||||
TrackDetailsItem(
|
||||
modifier = Modifier.weight(1F),
|
||||
text = startDate,
|
||||
placeholder = stringResource(MR.strings.track_started_reading_date),
|
||||
onClick = onStartDateClick,
|
||||
)
|
||||
VerticalDivider()
|
||||
TrackDetailsItem(
|
||||
modifier = Modifier.weight(1F),
|
||||
text = endDate,
|
||||
placeholder = stringResource(MR.strings.track_finished_reading_date),
|
||||
onClick = onEndDateClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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(MR.strings.add_tracking))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun TrackInfoDialogHomePreviews(
|
||||
@PreviewParameter(AnimeTrackInfoDialogHomePreviewProvider::class)
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
TachiyomiPreviewTheme { content() }
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package eu.kanade.presentation.track.anime
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import eu.kanade.tachiyomi.ui.entries.anime.track.AnimeTrackItem
|
||||
import eu.kanade.test.DummyTracker
|
||||
import tachiyomi.domain.track.anime.model.AnimeTrack
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
|
||||
internal class AnimeTrackInfoDialogHomePreviewProvider :
|
||||
PreviewParameterProvider<@Composable () -> Unit> {
|
||||
|
||||
private val aTrack = AnimeTrack(
|
||||
id = 1L,
|
||||
animeId = 2L,
|
||||
trackerId = 3L,
|
||||
remoteId = 4L,
|
||||
libraryId = null,
|
||||
title = "Manage Name On Tracker Site",
|
||||
lastEpisodeSeen = 2.0,
|
||||
totalEpisodes = 12L,
|
||||
status = 1L,
|
||||
score = 2.0,
|
||||
remoteUrl = "https://example.com",
|
||||
startDate = 0L,
|
||||
finishDate = 0L,
|
||||
)
|
||||
private val trackItemWithoutTrack = AnimeTrackItem(
|
||||
track = null,
|
||||
tracker = DummyTracker(
|
||||
id = 1L,
|
||||
name = "Example Tracker",
|
||||
),
|
||||
)
|
||||
private val trackItemWithTrack = AnimeTrackItem(
|
||||
track = aTrack,
|
||||
tracker = DummyTracker(
|
||||
id = 2L,
|
||||
name = "Example Tracker 2",
|
||||
),
|
||||
)
|
||||
|
||||
private val trackersWithAndWithoutTrack = @Composable {
|
||||
AnimeTrackInfoDialogHome(
|
||||
trackItems = listOf(
|
||||
trackItemWithoutTrack,
|
||||
trackItemWithTrack,
|
||||
),
|
||||
dateFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM),
|
||||
onStatusClick = {},
|
||||
onEpisodeClick = {},
|
||||
onScoreClick = {},
|
||||
onStartDateEdit = {},
|
||||
onEndDateEdit = {},
|
||||
onNewSearch = {},
|
||||
onOpenInBrowser = {},
|
||||
onRemoved = {},
|
||||
)
|
||||
}
|
||||
|
||||
private val noTrackers = @Composable {
|
||||
AnimeTrackInfoDialogHome(
|
||||
trackItems = listOf(),
|
||||
dateFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM),
|
||||
onStatusClick = {},
|
||||
onEpisodeClick = {},
|
||||
onScoreClick = {},
|
||||
onStartDateEdit = {},
|
||||
onEndDateEdit = {},
|
||||
onNewSearch = {},
|
||||
onOpenInBrowser = {},
|
||||
onRemoved = {},
|
||||
)
|
||||
}
|
||||
|
||||
override val values: Sequence<@Composable () -> Unit>
|
||||
get() = sequenceOf(
|
||||
trackersWithAndWithoutTrack,
|
||||
noTrackers,
|
||||
)
|
||||
}
|
Loading…
Reference in a new issue