mirror of
https://github.com/mihonapp/mihon.git
synced 2025-08-19 21:11:31 +02:00
Compare commits
3 Commits
27cad5275e
...
3789e13489
Author | SHA1 | Date | |
---|---|---|---|
|
3789e13489 | ||
|
30ccce468e | ||
|
361bc4fe2e |
@@ -2,6 +2,10 @@ package eu.kanade.presentation.updates
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.calculateEndPadding
|
||||
import androidx.compose.foundation.layout.calculateStartPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
@@ -13,7 +17,10 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.components.AppBarTitle
|
||||
@@ -29,12 +36,36 @@ import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.FastScrollLazyColumn
|
||||
import tachiyomi.presentation.core.components.ListGroupHeader
|
||||
import tachiyomi.presentation.core.components.TwoPanelBox
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import java.time.LocalDate
|
||||
|
||||
@Composable
|
||||
fun UpdateUpcomingScreen(
|
||||
state: UpdateUpcomingScreenModel.State,
|
||||
modifier: Modifier = Modifier,
|
||||
isTabletUi: Boolean = false,
|
||||
onClickUpcoming: (manga: Manga) -> Unit = {},
|
||||
) {
|
||||
if (!isTabletUi) {
|
||||
UpdateUpcomingScreenSmallImpl(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
onClickUpcoming = onClickUpcoming,
|
||||
)
|
||||
} else {
|
||||
UpdateUpcomingScreenLargeImpl(
|
||||
state = state,
|
||||
isTabletUi = isTabletUi,
|
||||
modifier = modifier,
|
||||
onClickUpcoming = onClickUpcoming,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun UpdateUpcomingScreenSmallImpl(
|
||||
state: UpdateUpcomingScreenModel.State,
|
||||
modifier: Modifier = Modifier,
|
||||
onClickUpcoming: (manga: Manga) -> Unit = {},
|
||||
@@ -46,7 +77,7 @@ fun UpdateUpcomingScreen(
|
||||
},
|
||||
) { paddingValues ->
|
||||
|
||||
UpdateUpcomingContent(
|
||||
UpdateUpcomingSmallContent(
|
||||
upcoming = state.items,
|
||||
events = state.events,
|
||||
contentPadding = paddingValues,
|
||||
@@ -89,7 +120,7 @@ internal fun UpdateUpcomingToolbar(
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun UpdateUpcomingContent(
|
||||
internal fun UpdateUpcomingSmallContent(
|
||||
upcoming: ImmutableList<UpcomingUIModel>,
|
||||
contentPadding: PaddingValues,
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -104,14 +135,19 @@ internal fun UpdateUpcomingContent(
|
||||
.filter { it.value is UpcomingUIModel.Header }
|
||||
.associate { Pair((it.value as UpcomingUIModel.Header).date, it.index + 1) } // Offset 1 for Calendar
|
||||
|
||||
val configuration = LocalConfiguration.current
|
||||
|
||||
FastScrollLazyColumn(
|
||||
contentPadding = contentPadding,
|
||||
state = listState,
|
||||
modifier = modifier,
|
||||
) {
|
||||
item {
|
||||
item(
|
||||
key = "upcoming-calendar",
|
||||
) {
|
||||
Calendar(
|
||||
events = events,
|
||||
screenWidth = configuration.screenWidthDp.dp,
|
||||
) { date ->
|
||||
dateToHeaderMap[date]?.let {
|
||||
coroutineScope.launch {
|
||||
@@ -122,7 +158,119 @@ internal fun UpdateUpcomingContent(
|
||||
}
|
||||
items(
|
||||
items = upcoming,
|
||||
key = null,
|
||||
key = { "upcoming-${it.hashCode()}" },
|
||||
contentType = {
|
||||
when (it) {
|
||||
is UpcomingUIModel.Header -> "header"
|
||||
is UpcomingUIModel.Item -> "item"
|
||||
}
|
||||
},
|
||||
) { item ->
|
||||
when (item) {
|
||||
is UpcomingUIModel.Item -> {
|
||||
UpcomingItem(
|
||||
upcoming = item.item,
|
||||
onClick = onClickUpcoming,
|
||||
)
|
||||
}
|
||||
|
||||
is UpcomingUIModel.Header -> {
|
||||
ListGroupHeader(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
text = relativeDateText(item.date),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun UpdateUpcomingScreenLargeImpl(
|
||||
state: UpdateUpcomingScreenModel.State,
|
||||
isTabletUi: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
onClickUpcoming: (manga: Manga) -> Unit = {},
|
||||
) {
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
topBar = { UpdateUpcomingToolbar() },
|
||||
) { contentPadding ->
|
||||
TwoPanelBox(
|
||||
modifier = Modifier.padding(
|
||||
start = contentPadding.calculateStartPadding(layoutDirection),
|
||||
end = contentPadding.calculateEndPadding(layoutDirection),
|
||||
),
|
||||
startContent = {
|
||||
UpdateUpcomingLargeCalendar(
|
||||
upcoming = state.items,
|
||||
listState = listState,
|
||||
isTabletUi = isTabletUi,
|
||||
events = state.events,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
},
|
||||
endContent = {
|
||||
UpdateUpcomingLargeContent(
|
||||
upcoming = state.items,
|
||||
listState = listState,
|
||||
contentPadding = contentPadding,
|
||||
onClickUpcoming = onClickUpcoming,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun UpdateUpcomingLargeCalendar(
|
||||
upcoming: ImmutableList<UpcomingUIModel>,
|
||||
isTabletUi: Boolean,
|
||||
listState: LazyListState,
|
||||
modifier: Modifier = Modifier,
|
||||
events: ImmutableMap<LocalDate, Int> = persistentMapOf(),
|
||||
) {
|
||||
val configuration = LocalConfiguration.current
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
val dateToHeaderMap =
|
||||
upcoming.withIndex()
|
||||
.filter { it.value is UpcomingUIModel.Header }
|
||||
.associate { Pair((it.value as UpcomingUIModel.Header).date, it.index) }
|
||||
|
||||
Calendar(
|
||||
modifier = modifier,
|
||||
events = events,
|
||||
isTabletUi = isTabletUi,
|
||||
screenWidth = configuration.screenWidthDp.dp,
|
||||
) { date ->
|
||||
dateToHeaderMap[date]?.let {
|
||||
coroutineScope.launch {
|
||||
listState.animateScrollToItem(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun UpdateUpcomingLargeContent(
|
||||
upcoming: ImmutableList<UpcomingUIModel>,
|
||||
listState: LazyListState,
|
||||
contentPadding: PaddingValues,
|
||||
modifier: Modifier = Modifier,
|
||||
onClickUpcoming: (manga: Manga) -> Unit,
|
||||
) {
|
||||
FastScrollLazyColumn(
|
||||
contentPadding = contentPadding,
|
||||
state = listState,
|
||||
modifier = modifier,
|
||||
) {
|
||||
items(
|
||||
items = upcoming,
|
||||
key = { "upcoming-${it.hashCode()}" },
|
||||
contentType = {
|
||||
when (it) {
|
||||
is UpcomingUIModel.Header -> "header"
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package eu.kanade.presentation.updates.components.calendar
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
@@ -14,27 +15,35 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import kotlinx.collections.immutable.ImmutableMap
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.time.DayOfWeek
|
||||
import java.time.LocalDate
|
||||
import java.time.Month
|
||||
import java.time.format.TextStyle
|
||||
import java.util.Locale
|
||||
import kotlin.math.ceil
|
||||
|
||||
private val CalenderPadding = 8.dp
|
||||
private val FontSize = 16.sp
|
||||
private val CalculatedHeight = 302.dp
|
||||
private const val ExtendedScale = 0.31f
|
||||
private const val MediumScale = 0.60f
|
||||
private const val HeightMultiplier = 68
|
||||
private const val DaysOfWeek = 7
|
||||
|
||||
@Composable
|
||||
fun Calendar(
|
||||
events: ImmutableMap<LocalDate, Int>,
|
||||
screenWidth: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
isTabletUi: Boolean = false,
|
||||
labelFormat: (DayOfWeek) -> String = {
|
||||
it.getDisplayName(
|
||||
TextStyle.SHORT,
|
||||
@@ -50,15 +59,19 @@ fun Calendar(
|
||||
val currentMonth = displayedMonth.value
|
||||
val currentYear = displayedYear.intValue
|
||||
|
||||
val daysInMonth = currentMonth.length(true)
|
||||
val startDayOfMonth = LocalDate.of(currentYear, currentMonth, 1)
|
||||
val firstDayOfMonth = startDayOfMonth.dayOfWeek
|
||||
val widthModifier = when {
|
||||
isTabletUi -> 1.0f
|
||||
screenWidth > 840.dp -> ExtendedScale
|
||||
screenWidth > 600.dp -> MediumScale
|
||||
else -> 1.0f
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.wrapContentHeight()
|
||||
.fillMaxWidth()
|
||||
.padding(all = CalenderPadding),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
CalenderHeader(
|
||||
month = currentMonth,
|
||||
@@ -73,31 +86,75 @@ fun Calendar(
|
||||
},
|
||||
)
|
||||
Spacer(modifier = Modifier.padding(vertical = 4.dp))
|
||||
LazyVerticalGrid(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(CalculatedHeight),
|
||||
columns = GridCells.Fixed(DaysOfWeek),
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
) {
|
||||
items(weekValue) { item ->
|
||||
Text(
|
||||
modifier = Modifier,
|
||||
text = labelFormat(item),
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
fontSize = FontSize,
|
||||
)
|
||||
}
|
||||
CalendarGrid(
|
||||
weekValue = weekValue,
|
||||
labelFormat = labelFormat,
|
||||
currentMonth = currentMonth,
|
||||
currentYear = currentYear,
|
||||
isTabletUi = isTabletUi,
|
||||
events = events,
|
||||
widthModifier = widthModifier,
|
||||
onClickDay = onClickDay,
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
items((getFirstDayOfMonth(firstDayOfMonth)..daysInMonth).toList()) {
|
||||
if (it > 0) {
|
||||
val localDate = LocalDate.of(currentYear, currentMonth, it)
|
||||
CalendarDay(
|
||||
date = localDate,
|
||||
onDayClick = onClickDay,
|
||||
events = events[localDate] ?: 0,
|
||||
)
|
||||
}
|
||||
@Composable
|
||||
private fun CalendarGrid(
|
||||
weekValue: Array<DayOfWeek>,
|
||||
labelFormat: (DayOfWeek) -> String,
|
||||
currentMonth: Month,
|
||||
currentYear: Int,
|
||||
isTabletUi: Boolean,
|
||||
events: ImmutableMap<LocalDate, Int>,
|
||||
modifier: Modifier = Modifier,
|
||||
onClickDay: (day: LocalDate) -> Unit = {},
|
||||
widthModifier: Float = 1.0F,
|
||||
) {
|
||||
val daysInMonth = currentMonth.length(true)
|
||||
val startDayOfMonth = LocalDate.of(currentYear, currentMonth, 1)
|
||||
val firstDayOfMonth = startDayOfMonth.dayOfWeek
|
||||
|
||||
val dayEntries = (getFirstDayOfMonth(firstDayOfMonth)..daysInMonth).toImmutableList()
|
||||
val height = (((((dayEntries.size - 1) / DaysOfWeek) + ceil(1.0f - widthModifier)) * HeightMultiplier)).dp
|
||||
|
||||
val modeModifier = if (isTabletUi) {
|
||||
modifier
|
||||
.fillMaxWidth(widthModifier)
|
||||
.wrapContentHeight()
|
||||
} else {
|
||||
modifier
|
||||
.fillMaxWidth(widthModifier)
|
||||
.height(height)
|
||||
}
|
||||
|
||||
LazyVerticalGrid(
|
||||
modifier = modeModifier,
|
||||
columns = GridCells.Fixed(DaysOfWeek),
|
||||
) {
|
||||
items(weekValue) { item ->
|
||||
Text(
|
||||
modifier = Modifier,
|
||||
text = labelFormat(item),
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
fontSize = FontSize,
|
||||
)
|
||||
}
|
||||
|
||||
items(dayEntries) {
|
||||
if (it > 0) {
|
||||
val localDate = LocalDate.of(currentYear, currentMonth, it)
|
||||
CalendarDay(
|
||||
date = localDate,
|
||||
onDayClick = onClickDay,
|
||||
events = events[localDate] ?: 0,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.updates.UpdateUpcomingScreen
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.presentation.util.isTabletUi
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
||||
|
||||
class UpdateUpcomingScreen : Screen() {
|
||||
@@ -23,6 +24,7 @@ class UpdateUpcomingScreen : Screen() {
|
||||
|
||||
UpdateUpcomingScreen(
|
||||
state = state,
|
||||
isTabletUi = isTabletUi(),
|
||||
onClickUpcoming = { navigator.push(MangaScreen(it.id)) },
|
||||
)
|
||||
}
|
||||
|
Reference in New Issue
Block a user