Skip to content

Commit

Permalink
Add Quantity Badge to Upcoming Screen (#1250)
Browse files Browse the repository at this point in the history
Co-authored-by: AntsyLich <[email protected]>
  • Loading branch information
Animeboynz and AntsyLich authored Oct 12, 2024
1 parent 7c7af72 commit 6b2bba4
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 12 deletions.
20 changes: 19 additions & 1 deletion app/src/main/java/eu/kanade/core/util/CollectionUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract

fun <T : R, R : Any> List<T>.insertSeparators(
generator: (T?, T?) -> R?,
generator: (before: T?, after: T?) -> R?,
): List<R> {
if (isEmpty()) return emptyList()
val newList = mutableListOf<R>()
Expand All @@ -19,6 +19,24 @@ fun <T : R, R : Any> List<T>.insertSeparators(
return newList
}

/**
* Similar to [eu.kanade.core.util.insertSeparators] but iterates from last to first element
*/
fun <T : R, R : Any> List<T>.insertSeparatorsReversed(
generator: (before: T?, after: T?) -> R?,
): List<R> {
if (isEmpty()) return emptyList()
val newList = mutableListOf<R>()
for (i in size downTo 0) {
val after = getOrNull(i)
after?.let(newList::add)
val before = getOrNull(i - 1)
val separator = generator.invoke(before, after)
separator?.let(newList::add)
}
return newList.asReversed()
}

fun <E> HashSet<E>.addOrRemove(value: E, shouldAdd: Boolean) {
if (shouldAdd) {
add(value)
Expand Down
46 changes: 43 additions & 3 deletions app/src/main/java/mihon/feature/upcoming/UpcomingScreenContent.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
package mihon.feature.upcoming

import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
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
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
import androidx.compose.material3.Badge
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.font.FontWeight
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.components.AppBar
Expand All @@ -27,9 +34,9 @@ import tachiyomi.core.common.Constants
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.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource
import java.time.LocalDate
import java.time.YearMonth
Expand Down Expand Up @@ -99,6 +106,33 @@ private fun UpcomingToolbar() {
)
}

@Composable
private fun DateHeading(
date: LocalDate,
mangaCount: Int,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth(),
) {
Text(
text = relativeDateText(date),
modifier = Modifier
.padding(MaterialTheme.padding.small)
.padding(start = MaterialTheme.padding.small),
color = MaterialTheme.colorScheme.onSurfaceVariant,
fontWeight = FontWeight.SemiBold,
style = MaterialTheme.typography.bodyMedium,
)
Badge(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary,
) {
Text("$mangaCount")
}
}
}

@Composable
private fun UpcomingScreenSmallImpl(
listState: LazyListState,
Expand Down Expand Up @@ -140,7 +174,10 @@ private fun UpcomingScreenSmallImpl(
)
}
is UpcomingUIModel.Header -> {
ListGroupHeader(text = relativeDateText(item.date))
DateHeading(
date = item.date,
mangaCount = item.mangaCount,
)
}
}
}
Expand Down Expand Up @@ -188,7 +225,10 @@ private fun UpcomingScreenLargeImpl(
)
}
is UpcomingUIModel.Header -> {
ListGroupHeader(text = relativeDateText(item.date))
DateHeading(
date = item.date,
mangaCount = item.mangaCount,
)
}
}
}
Expand Down
17 changes: 10 additions & 7 deletions app/src/main/java/mihon/feature/upcoming/UpcomingScreenModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import androidx.compose.ui.util.fastMap
import androidx.compose.ui.util.fastMapIndexedNotNull
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import eu.kanade.core.util.insertSeparators
import eu.kanade.core.util.insertSeparatorsReversed
import eu.kanade.tachiyomi.util.lang.toLocalDate
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableMap
Expand Down Expand Up @@ -33,7 +33,7 @@ class UpcomingScreenModel(
val upcomingItems = it.toUpcomingUIModels()
state.copy(
items = upcomingItems,
events = it.toEvents(),
events = upcomingItems.toEvents(),
headerIndexes = upcomingItems.getHeaderIndexes(),
)
}
Expand All @@ -42,23 +42,26 @@ class UpcomingScreenModel(
}

private fun List<Manga>.toUpcomingUIModels(): ImmutableList<UpcomingUIModel> {
var mangaCount = 0
return fastMap { UpcomingUIModel.Item(it) }
.insertSeparators { before, after ->
.insertSeparatorsReversed { before, after ->
if (after != null) mangaCount++

val beforeDate = before?.manga?.expectedNextUpdate?.toLocalDate()
val afterDate = after?.manga?.expectedNextUpdate?.toLocalDate()

if (beforeDate != afterDate && afterDate != null) {
UpcomingUIModel.Header(afterDate)
UpcomingUIModel.Header(afterDate, mangaCount).also { mangaCount = 0 }
} else {
null
}
}
.toImmutableList()
}

private fun List<Manga>.toEvents(): ImmutableMap<LocalDate, Int> {
return groupBy { it.expectedNextUpdate?.toLocalDate() ?: LocalDate.MAX }
.mapValues { it.value.size }
private fun List<UpcomingUIModel>.toEvents(): ImmutableMap<LocalDate, Int> {
return filterIsInstance<UpcomingUIModel.Header>()
.associate { it.date to it.mangaCount }
.toImmutableMap()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import tachiyomi.domain.manga.model.Manga
import java.time.LocalDate

sealed interface UpcomingUIModel {
data class Header(val date: LocalDate) : UpcomingUIModel
data class Header(val date: LocalDate, val mangaCount: Int) : UpcomingUIModel
data class Item(val manga: Manga) : UpcomingUIModel
}

0 comments on commit 6b2bba4

Please sign in to comment.