feat (ui): showing source icon in library (#261)

* showing source icon in library

* Change language icon for local source from Folder to Globe

* adjust size & padding for icon & badge

* Setting enable/disable source icon
This commit is contained in:
Tran M. Cuong 2024-08-19 12:50:16 +07:00 committed by GitHub
parent 49e1759a05
commit 29eeccad1b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 142 additions and 4 deletions

View file

@ -34,6 +34,7 @@ import tachiyomi.domain.library.model.LibrarySort
import tachiyomi.domain.library.model.sort import tachiyomi.domain.library.model.sort
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.i18n.kmk.KMR
import tachiyomi.i18n.sy.SYMR import tachiyomi.i18n.sy.SYMR
import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.CheckboxItem
import tachiyomi.presentation.core.components.HeadingItem import tachiyomi.presentation.core.components.HeadingItem
@ -311,6 +312,12 @@ private fun ColumnScope.DisplayPage(
label = stringResource(MR.strings.action_display_language_badge), label = stringResource(MR.strings.action_display_language_badge),
pref = screenModel.libraryPreferences.languageBadge(), pref = screenModel.libraryPreferences.languageBadge(),
) )
// KMK -->
CheckboxItem(
label = stringResource(KMR.strings.action_display_source_badge),
pref = screenModel.libraryPreferences.sourceBadge(),
)
// KMK <--
CheckboxItem( CheckboxItem(
label = stringResource(MR.strings.action_display_show_continue_reading_button), label = stringResource(MR.strings.action_display_show_continue_reading_button),
pref = screenModel.libraryPreferences.showContinueReadingButton(), pref = screenModel.libraryPreferences.showContinueReadingButton(),

View file

@ -1,13 +1,23 @@
package eu.kanade.presentation.library.components package eu.kanade.presentation.library.components
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.height
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Warning
import androidx.compose.material.icons.outlined.Folder import androidx.compose.material.icons.outlined.Folder
import androidx.compose.material.icons.outlined.Language
import androidx.compose.material.icons.outlined.LocalLibrary
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import eu.kanade.domain.source.model.icon
import eu.kanade.presentation.theme.TachiyomiPreviewTheme import eu.kanade.presentation.theme.TachiyomiPreviewTheme
import tachiyomi.domain.source.model.Source
import tachiyomi.presentation.core.components.Badge import tachiyomi.presentation.core.components.Badge
import tachiyomi.source.local.isLocal
@Composable @Composable
internal fun DownloadsBadge(count: Long) { internal fun DownloadsBadge(count: Long) {
@ -34,7 +44,7 @@ internal fun LanguageBadge(
) { ) {
if (isLocal) { if (isLocal) {
Badge( Badge(
imageVector = Icons.Outlined.Folder, imageVector = Icons.Outlined.Language,
color = MaterialTheme.colorScheme.tertiary, color = MaterialTheme.colorScheme.tertiary,
iconColor = MaterialTheme.colorScheme.onTertiary, iconColor = MaterialTheme.colorScheme.onTertiary,
) )
@ -47,6 +57,46 @@ internal fun LanguageBadge(
} }
} }
// KMK -->
@Composable
fun SourceIconBadge(
source: Source?,
) {
if (source == null) return
val icon = source.icon
when {
source.isStub && icon == null -> {
Badge(
imageVector = Icons.Filled.Warning,
iconColor = MaterialTheme.colorScheme.error,
)
}
icon != null -> {
Badge(
imageBitmap = icon,
modifier = Modifier.scale(1.3f).height(18.dp),
)
}
source.isLocal() -> {
Badge(
imageVector = Icons.Outlined.Folder,
color = MaterialTheme.colorScheme.tertiary,
iconColor = MaterialTheme.colorScheme.onTertiary,
)
}
else -> {
// Default source icon (if source doesn't have an icon)
Badge(
imageVector = Icons.Outlined.LocalLibrary,
color = MaterialTheme.colorScheme.tertiary,
iconColor = MaterialTheme.colorScheme.onTertiary,
)
}
}
}
// KMK <--
@PreviewLightDark @PreviewLightDark
@Composable @Composable
private fun BadgePreview() { private fun BadgePreview() {

View file

@ -53,6 +53,9 @@ internal fun LibraryComfortableGrid(
isLocal = libraryItem.isLocal, isLocal = libraryItem.isLocal,
sourceLanguage = libraryItem.sourceLanguage, sourceLanguage = libraryItem.sourceLanguage,
) )
// KMK -->
SourceIconBadge(source = libraryItem.source)
// KMK <--
}, },
onLongClick = { onLongClick(libraryItem.libraryManga) }, onLongClick = { onLongClick(libraryItem.libraryManga) },
onClick = { onClick(libraryItem.libraryManga) }, onClick = { onClick(libraryItem.libraryManga) },

View file

@ -54,6 +54,9 @@ internal fun LibraryCompactGrid(
isLocal = libraryItem.isLocal, isLocal = libraryItem.isLocal,
sourceLanguage = libraryItem.sourceLanguage, sourceLanguage = libraryItem.sourceLanguage,
) )
// KMK -->
SourceIconBadge(source = libraryItem.source)
// KMK <--
}, },
onLongClick = { onLongClick(libraryItem.libraryManga) }, onLongClick = { onLongClick(libraryItem.libraryManga) },
onClick = { onClick(libraryItem.libraryManga) }, onClick = { onClick(libraryItem.libraryManga) },

View file

@ -61,6 +61,9 @@ internal fun LibraryList(
isLocal = libraryItem.isLocal, isLocal = libraryItem.isLocal,
sourceLanguage = libraryItem.sourceLanguage, sourceLanguage = libraryItem.sourceLanguage,
) )
// KMK -->
SourceIconBadge(source = libraryItem.source)
// KMK <--
}, },
onLongClick = { onLongClick(libraryItem.libraryManga) }, onLongClick = { onLongClick(libraryItem.libraryManga) },
onClick = { onClick(libraryItem.libraryManga) }, onClick = { onClick(libraryItem.libraryManga) },

View file

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.library
import eu.kanade.tachiyomi.source.getNameForMangaInfo import eu.kanade.tachiyomi.source.getNameForMangaInfo
import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.library.model.LibraryManga
import tachiyomi.domain.source.model.Source
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -12,6 +13,9 @@ data class LibraryItem(
val unreadCount: Long = -1, val unreadCount: Long = -1,
val isLocal: Boolean = false, val isLocal: Boolean = false,
val sourceLanguage: String = "", val sourceLanguage: String = "",
// KMK -->
val source: Source? = null,
// KMK <--
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager = Injekt.get(),
) { ) {
/** /**

View file

@ -107,6 +107,7 @@ import tachiyomi.domain.manga.model.CustomMangaInfo
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaUpdate import tachiyomi.domain.manga.model.MangaUpdate
import tachiyomi.domain.manga.model.applyFilter import tachiyomi.domain.manga.model.applyFilter
import tachiyomi.domain.source.model.StubSource
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.interactor.GetTracksPerManga import tachiyomi.domain.track.interactor.GetTracksPerManga
@ -118,6 +119,7 @@ import tachiyomi.source.local.isLocal
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.Collections import java.util.Collections
import tachiyomi.domain.source.model.Source as DomainSource
/** /**
* Typealias for the library manga, using the category as keys, and list of manga as values. * Typealias for the library manga, using the category as keys, and list of manga as values.
@ -498,6 +500,9 @@ class LibraryScreenModel(
// SY --> // SY -->
libraryPreferences.filterLewd().changes(), libraryPreferences.filterLewd().changes(),
// SY <-- // SY <--
// KMK -->
libraryPreferences.sourceBadge().changes(),
// KMK <--
) { ) {
ItemPreferences( ItemPreferences(
downloadBadge = it[0] as Boolean, downloadBadge = it[0] as Boolean,
@ -514,6 +519,9 @@ class LibraryScreenModel(
// SY --> // SY -->
filterLewd = it[11] as TriState, filterLewd = it[11] as TriState,
// SY <-- // SY <--
// KMK -->
sourceBadge = it[12] as Boolean,
// KMK <--
) )
} }
} }
@ -530,6 +538,9 @@ class LibraryScreenModel(
libraryMangaList libraryMangaList
.map { libraryManga -> .map { libraryManga ->
// Display mode based on user preference: take it from global library setting or category // Display mode based on user preference: take it from global library setting or category
// KMK -->
val source = sourceManager.getOrStub(libraryManga.manga.source)
// KMK <--
LibraryItem( LibraryItem(
libraryManga, libraryManga,
downloadCount = if (prefs.downloadBadge) { downloadCount = if (prefs.downloadBadge) {
@ -548,10 +559,23 @@ class LibraryScreenModel(
unreadCount = libraryManga.unreadCount, unreadCount = libraryManga.unreadCount,
isLocal = if (prefs.localBadge) libraryManga.manga.isLocal() else false, isLocal = if (prefs.localBadge) libraryManga.manga.isLocal() else false,
sourceLanguage = if (prefs.languageBadge) { sourceLanguage = if (prefs.languageBadge) {
sourceManager.getOrStub(libraryManga.manga.source).lang source.lang
} else { } else {
"" ""
}, },
// KMK -->
source = if (prefs.sourceBadge) {
DomainSource(
source.id,
source.lang,
source.name,
supportsLatest = false,
isStub = source is StubSource
)
} else {
null
},
// KMK <--
) )
} }
.groupBy { it.libraryManga.category } .groupBy { it.libraryManga.category }
@ -1312,6 +1336,9 @@ class LibraryScreenModel(
val downloadBadge: Boolean, val downloadBadge: Boolean,
val localBadge: Boolean, val localBadge: Boolean,
val languageBadge: Boolean, val languageBadge: Boolean,
// KMK -->
val sourceBadge: Boolean,
// KMK <--
val skipOutsideReleasePeriod: Boolean, val skipOutsideReleasePeriod: Boolean,
val globalFilterDownloaded: Boolean, val globalFilterDownloaded: Boolean,

View file

@ -127,7 +127,11 @@ class LibraryPreferences(
fun localBadge() = preferenceStore.getBoolean("display_local_badge", true) fun localBadge() = preferenceStore.getBoolean("display_local_badge", true)
fun languageBadge() = preferenceStore.getBoolean("display_language_badge", false) fun languageBadge() = preferenceStore.getBoolean("display_language_badge", true)
// KMK -->
fun sourceBadge() = preferenceStore.getBoolean("display_source_badge", true)
// KMK <--
fun newShowUpdatesCount() = preferenceStore.getBoolean("library_show_updates_count", true) fun newShowUpdatesCount() = preferenceStore.getBoolean("library_show_updates_count", true)
fun newUpdatesCount() = preferenceStore.getInt(Preference.appStateKey("library_unseen_updates_count"), 0) fun newUpdatesCount() = preferenceStore.getInt(Preference.appStateKey("library_unseen_updates_count"), 0)

View file

@ -40,6 +40,7 @@
<!-- Library section --> <!-- Library section -->
<string name="pref_show_updating_progress_banner">Show updating progress banner</string> <string name="pref_show_updating_progress_banner">Show updating progress banner</string>
<string name="action_display_source_badge">Source icon</string>
<!-- Extension section --> <!-- Extension section -->
<string name="ext_unofficial">Unofficial</string> <string name="ext_unofficial">Unofficial</string>

View file

@ -1,8 +1,11 @@
package tachiyomi.presentation.core.components package tachiyomi.presentation.core.components
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.InlineTextContent import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.appendInlineContent import androidx.compose.foundation.text.appendInlineContent
@ -10,9 +13,12 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
@ -29,7 +35,12 @@ fun BadgeGroup(
shape: Shape = MaterialTheme.shapes.extraSmall, shape: Shape = MaterialTheme.shapes.extraSmall,
content: @Composable RowScope.() -> Unit, content: @Composable RowScope.() -> Unit,
) { ) {
Row(modifier = modifier.clip(shape)) { Row(
modifier = modifier.clip(shape)
// KMK -->
.height(18.dp),
// KMK <--
) {
content() content()
} }
} }
@ -99,3 +110,28 @@ fun Badge(
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
) )
} }
// KMK -->
@Composable
fun Badge(
imageBitmap: ImageBitmap,
modifier: Modifier = Modifier,
color: Color = MaterialTheme.colorScheme.secondary,
tint: Color? = null,
shape: Shape = RectangleShape,
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.clip(shape)
.background(color),
) {
Image(
bitmap = imageBitmap,
colorFilter = tint?.let { ColorFilter.tint(it) },
contentDescription = null,
modifier = modifier,
)
}
}
// KMK <--