Compare commits

...

2 Commits

Author SHA1 Message Date
Matthew Witman
1024cbdc95 A Bunch of changes from the next round of reviews 2024-03-28 00:50:05 -04:00
Matthew Witman
38770d2563 changed access modifier 2024-03-27 19:38:32 -04:00
6 changed files with 91 additions and 121 deletions

View File

@@ -32,7 +32,7 @@ import mihon.domain.extensionrepo.interactor.ReplaceExtensionRepo
import mihon.domain.extensionrepo.interactor.UpdateExtensionRepo
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
import mihon.domain.extensionrepo.service.ExtensionRepoService
import mihon.domain.manga.interactor.GetUpcomingManga
import mihon.domain.upcoming.interactor.GetUpcomingManga
import tachiyomi.data.category.CategoryRepositoryImpl
import tachiyomi.data.chapter.ChapterRepositoryImpl
import tachiyomi.data.history.HistoryRepositoryImpl

View File

@@ -28,7 +28,6 @@ import eu.kanade.presentation.components.relativeDateText
import eu.kanade.presentation.util.isTabletUi
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.coroutines.launch
import mihon.feature.upcoming.components.UpcomingItem
import mihon.feature.upcoming.components.calendar.Calendar
@@ -44,8 +43,8 @@ import java.time.LocalDate
@Composable
fun UpcomingScreenContent(
onClickUpcoming: (manga: Manga) -> Unit,
state: UpcomingScreenModel.State,
onClickUpcoming: (manga: Manga) -> Unit,
modifier: Modifier = Modifier,
) {
Scaffold(
@@ -69,20 +68,12 @@ fun UpcomingScreenContent(
}
@Composable
internal fun UpcomingToolbar(
private fun UpcomingToolbar(
modifier: Modifier = Modifier,
) {
val navigator = LocalNavigator.currentOrThrow
Column(
modifier = modifier,
) {
Column(modifier = modifier) {
TopAppBar(
navigationIcon = {
IconButton(onClick = navigator::pop) {
UpIcon()
}
},
title = { AppBarTitle(stringResource(MR.strings.label_upcoming)) },
actions = {
val uriHandler = LocalUriHandler.current
IconButton(onClick = { uriHandler.openUri(Constants.URL_HELP_UPCOMING) }) {
@@ -92,63 +83,47 @@ internal fun UpcomingToolbar(
)
}
},
navigationIcon = {
IconButton(onClick = navigator::pop) {
UpIcon()
}
},
title = { AppBarTitle(stringResource(MR.strings.label_upcoming)) },
)
}
}
@Composable
internal fun UpcomingScreenSmallImpl(
private fun UpcomingScreenSmallImpl(
onClickUpcoming: (manga: Manga) -> Unit,
state: UpcomingScreenModel.State,
paddingValues: PaddingValues,
) {
UpcomingSmallContent(
upcoming = state.items,
events = state.events,
contentPadding = paddingValues,
onClickUpcoming = onClickUpcoming,
)
}
@Composable
internal fun UpcomingSmallContent(
contentPadding: PaddingValues,
onClickUpcoming: (manga: Manga) -> Unit,
upcoming: ImmutableList<UpcomingUIModel>,
modifier: Modifier = Modifier,
events: ImmutableMap<LocalDate, Int> = persistentMapOf(),
) {
val listState = rememberLazyListState()
val coroutineScope = rememberCoroutineScope()
val dateToHeaderMap =
upcoming.withIndex()
.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,
contentPadding = paddingValues,
state = listState,
modifier = modifier,
) {
item(
key = "upcoming-calendar",
) {
Calendar(
events = events,
events = state.events,
screenWidth = configuration.screenWidthDp.dp,
) { date ->
dateToHeaderMap[date]?.let {
coroutineScope.launch {
listState.animateScrollToItem(it)
onClickDay = { date ->
state.headerIndexes[date]?.let<Int, Unit> {
coroutineScope.launch {
listState.animateScrollToItem(it + 1)
}
}
}
}
},
)
}
items(
items = upcoming,
items = state.items,
key = { "upcoming-${it.hashCode()}" },
contentType = {
when (it) {
@@ -160,10 +135,11 @@ internal fun UpcomingSmallContent(
when (item) {
is UpcomingUIModel.Item -> {
UpcomingItem(
upcoming = item.item,
onClick = { onClickUpcoming(item.item) },
upcoming = item.manga,
onClick = { onClickUpcoming(item.manga) },
)
}
is UpcomingUIModel.Header -> {
ListGroupHeader(
modifier = Modifier.animateItemPlacement(),
@@ -176,10 +152,10 @@ internal fun UpcomingSmallContent(
}
@Composable
internal fun UpcomingScreenLargeImpl(
private fun UpcomingScreenLargeImpl(
state: UpcomingScreenModel.State,
onClickUpcoming: (manga: Manga) -> Unit,
paddingValues: PaddingValues,
state: UpcomingScreenModel.State,
) {
val layoutDirection = LocalLayoutDirection.current
val listState = rememberLazyListState()
@@ -191,9 +167,9 @@ internal fun UpcomingScreenLargeImpl(
),
startContent = {
UpcomingLargeCalendar(
upcoming = state.items,
listState = listState,
events = state.events,
headerIndexes = state.headerIndexes,
listState = listState,
modifier = Modifier.padding(paddingValues),
)
},
@@ -209,40 +185,36 @@ internal fun UpcomingScreenLargeImpl(
}
@Composable
internal fun UpcomingLargeCalendar(
upcoming: ImmutableList<UpcomingUIModel>,
private fun UpcomingLargeCalendar(
events: ImmutableMap<LocalDate, Int>,
headerIndexes: ImmutableMap<LocalDate, Int>,
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,
screenWidth = configuration.screenWidthDp.dp,
) { date ->
dateToHeaderMap[date]?.let {
coroutineScope.launch {
listState.animateScrollToItem(it)
onClickDay = { date ->
headerIndexes[date]?.let {
coroutineScope.launch {
listState.animateScrollToItem(it)
}
}
}
}
},
)
}
@Composable
internal fun UpcomingLargeContent(
private fun UpcomingLargeContent(
upcoming: ImmutableList<UpcomingUIModel>,
listState: LazyListState,
contentPadding: PaddingValues,
modifier: Modifier = Modifier,
onClickUpcoming: (manga: Manga) -> Unit,
modifier: Modifier = Modifier,
) {
FastScrollLazyColumn(
contentPadding = contentPadding,
@@ -262,10 +234,11 @@ internal fun UpcomingLargeContent(
when (item) {
is UpcomingUIModel.Item -> {
UpcomingItem(
upcoming = item.item,
onClick = { onClickUpcoming(item.item) },
upcoming = item.manga,
onClick = { onClickUpcoming(item.manga) },
)
}
is UpcomingUIModel.Header -> {
ListGroupHeader(
modifier = Modifier.animateItemPlacement(),
@@ -279,5 +252,5 @@ internal fun UpcomingLargeContent(
sealed interface UpcomingUIModel {
data class Header(val date: LocalDate) : UpcomingUIModel
data class Item(val item: Manga) : UpcomingUIModel
data class Item(val manga: Manga) : UpcomingUIModel
}

View File

@@ -13,7 +13,7 @@ import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import mihon.domain.manga.interactor.GetUpcomingManga
import mihon.domain.upcoming.interactor.GetUpcomingManga
import tachiyomi.domain.manga.model.Manga
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@@ -27,9 +27,11 @@ class UpcomingScreenModel(
screenModelScope.launch {
getUpcomingManga.subscribe().collectLatest {
mutableState.update { state ->
val upcomingItems = it.toUpcomingUIModels()
state.copy(
items = it.toUpcomingUIModels(),
items = upcomingItems,
events = it.toEvents(),
headerIndexes = getHeaderIndexes(upcomingItems),
)
}
}
@@ -39,8 +41,8 @@ class UpcomingScreenModel(
private fun List<Manga>.toUpcomingUIModels(): ImmutableList<UpcomingUIModel> {
return map { UpcomingUIModel.Item(it) }
.insertSeparators { before, after ->
val beforeDate = before?.item?.expectedNextUpdate?.toLocalDate()
val afterDate = after?.item?.expectedNextUpdate?.toLocalDate()
val beforeDate = before?.manga?.expectedNextUpdate?.toLocalDate()
val afterDate = after?.manga?.expectedNextUpdate?.toLocalDate()
when {
beforeDate != afterDate && afterDate != null -> UpcomingUIModel.Header(afterDate)
// Return null to avoid adding a separator between two items.
@@ -56,9 +58,16 @@ class UpcomingScreenModel(
.toImmutableMap()
}
private fun getHeaderIndexes(upcomingItems: List<UpcomingUIModel>): ImmutableMap<LocalDate, Int> {
return upcomingItems.withIndex()
.filter { it.value is UpcomingUIModel.Header }
.associate { Pair((it.value as UpcomingUIModel.Header).date, it.index) }
.toImmutableMap()
}
data class State(
val items: ImmutableList<UpcomingUIModel> = persistentListOf(),
val events: ImmutableMap<LocalDate, Int> = persistentMapOf(),
val headerIndexes: Map<LocalDate, Int> = persistentMapOf(),
val headerIndexes: ImmutableMap<LocalDate, Int> = persistentMapOf(),
)
}

View File

@@ -24,7 +24,6 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import eu.kanade.presentation.util.isTabletUi
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.toImmutableList
import tachiyomi.presentation.core.components.material.padding
@@ -58,13 +57,6 @@ fun Calendar(
var currentYearMonth by remember { mutableStateOf(YearMonth.now()) }
val isTabletUi = isTabletUi()
val localFirstDayOfWeek = WeekFields.of(Locale.getDefault()).firstDayOfWeek.value
val weekDays = remember {
(0 until DaysOfWeek)
.map { DayOfWeek.of(((localFirstDayOfWeek - 1 + it) % DaysOfWeek) + 1) }
.toImmutableList()
}
val widthModifier = when {
isTabletUi -> 1.0f
screenWidth > 840.dp -> ExtendedScale
@@ -88,7 +80,6 @@ fun Calendar(
modifier = Modifier.padding(horizontal = 8.dp),
) {
CalendarGrid(
weekDays = weekDays,
labelFormat = labelFormat,
currentYearMonth = currentYearMonth,
isTabletUi = isTabletUi,
@@ -103,34 +94,35 @@ fun Calendar(
@Composable
private fun CalendarGrid(
weekDays: ImmutableList<DayOfWeek>,
labelFormat: (DayOfWeek) -> String,
currentYearMonth: YearMonth,
isTabletUi: Boolean,
events: ImmutableMap<LocalDate, Int>,
onClickDay: (day: LocalDate) -> Unit,
widthModifier: Float,
modifier: Modifier = Modifier,
onClickDay: (day: LocalDate) -> Unit = {},
widthModifier: Float = 1.0F,
) {
val daysInMonth = currentYearMonth.lengthOfMonth()
val startDayOfMonth = currentYearMonth.atDay(1)
val firstDayOfMonth = startDayOfMonth.dayOfWeek
// The lower bound for Calendar Days, between -5 and 1 to provide cell offset
val dayEntries = (-weekDays.indexOf(firstDayOfMonth) + 1..daysInMonth).toImmutableList()
val height = (((dayEntries.size - 1) / DaysOfWeek + ceil(1.0f - widthModifier)) * HeightMultiplier).dp
val modeModifier = if (isTabletUi) {
modifier
.fillMaxWidth(widthModifier)
} else {
modifier
.fillMaxWidth(widthModifier)
.height(height)
val localeFirstDayOfWeek = WeekFields.of(Locale.getDefault()).firstDayOfWeek.value
val weekDays = remember {
(0 until DaysOfWeek)
.map { DayOfWeek.of((localeFirstDayOfWeek - 1 + it) % DaysOfWeek + 1) }
.toImmutableList()
}
val emptyFieldCount = weekDays.indexOf(currentYearMonth.atDay(1).dayOfWeek)
val daysInMonth = currentYearMonth.lengthOfMonth()
val height = (((emptyFieldCount + daysInMonth) / DaysOfWeek + ceil(1.0f - widthModifier)) * HeightMultiplier).dp
LazyVerticalGrid(
modifier = modeModifier,
modifier = if (isTabletUi) {
modifier
.fillMaxWidth(widthModifier)
} else {
modifier
.fillMaxWidth(widthModifier)
.height(height)
},
columns = GridCells.Fixed(DaysOfWeek),
) {
items(weekDays) { item ->
@@ -141,15 +133,14 @@ private fun CalendarGrid(
fontSize = FontSize,
)
}
items(dayEntries) {
if (it > 0) {
val localDate = currentYearMonth.atDay(it)
CalendarDay(
date = localDate,
onDayClick = { onClickDay(localDate) },
events = events[localDate] ?: 0,
)
}
items(emptyFieldCount) {}
items(daysInMonth) {
val localDate = currentYearMonth.atDay(it + 1)
CalendarDay(
date = localDate,
onDayClick = { onClickDay(localDate) },
events = events[localDate] ?: 0,
)
}
}
}

View File

@@ -43,7 +43,10 @@ fun CalenderHeader(
Row(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = MaterialTheme.padding.medium, vertical = MaterialTheme.padding.small),
.padding(
horizontal = MaterialTheme.padding.medium,
vertical = MaterialTheme.padding.small,
),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
@@ -87,12 +90,6 @@ private fun AnimatedContentTransitionScope<YearMonth>.getAnimation(): ContentTra
.using(SizeTransform(clip = false))
}
/**
* Returns the formatted title text for the Calendar header.
*
* @param monthYear The current month and year pair.
* @return The formatted title text.
*/
@Composable
@ReadOnlyComposable
private fun getTitleText(monthYear: YearMonth): String {

View File

@@ -1,4 +1,4 @@
package mihon.domain.manga.interactor
package mihon.domain.upcoming.interactor
import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.coroutines.flow.Flow