mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-15 15:02:49 +01:00
Merge branch 'master' into sync-part-final
This commit is contained in:
commit
3c95d3aefc
@ -81,9 +81,9 @@ class UpdateManga(
|
||||
dateTime: ZonedDateTime = ZonedDateTime.now(),
|
||||
window: Pair<Long, Long> = fetchInterval.getWindow(dateTime),
|
||||
): Boolean {
|
||||
return fetchInterval.toMangaUpdateOrNull(manga, dateTime, window)
|
||||
?.let { mangaRepository.update(it) }
|
||||
?: false
|
||||
return mangaRepository.update(
|
||||
fetchInterval.toMangaUpdate(manga, dateTime, window),
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun awaitUpdateLastUpdate(mangaId: Long): Boolean {
|
||||
|
@ -11,7 +11,7 @@ class CreateSourceRepo(private val preferences: SourcePreferences) {
|
||||
return Result.InvalidUrl
|
||||
}
|
||||
|
||||
preferences.extensionRepos() += name.substringBeforeLast("/index.min.json")
|
||||
preferences.extensionRepos() += name.removeSuffix("/index.min.json")
|
||||
|
||||
return Result.Success
|
||||
}
|
||||
|
@ -17,11 +17,15 @@ class TrustExtension(
|
||||
fun trust(pkgName: String, versionCode: Long, signatureHash: String) {
|
||||
preferences.trustedExtensions().getAndSet { exts ->
|
||||
// Remove previously trusted versions
|
||||
val removed = exts.filter { it.startsWith("$pkgName:") }.toMutableSet()
|
||||
val removed = exts.filterNot { it.startsWith("$pkgName:") }.toMutableSet()
|
||||
|
||||
removed.also {
|
||||
it += "$pkgName:$versionCode:$signatureHash"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun revokeAll() {
|
||||
preferences.trustedExtensions().delete()
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,9 @@ fun DbTrack.toDomainTrack(idRequired: Boolean = true): Track? {
|
||||
lastChapterRead = last_chapter_read.toDouble(),
|
||||
totalChapters = total_chapters.toLong(),
|
||||
status = status.toLong(),
|
||||
score = score.toDouble(),
|
||||
// Jank workaround due to precision issues while converting
|
||||
// See https://github.com/tachiyomiorg/tachiyomi/issues/10343
|
||||
score = score.toString().toDouble(),
|
||||
remoteUrl = tracking_url,
|
||||
startDate = started_reading_date,
|
||||
finishDate = finished_reading_date,
|
||||
|
@ -9,6 +9,7 @@ enum class AppTheme(val titleRes: StringResource?) {
|
||||
GREEN_APPLE(MR.strings.theme_greenapple),
|
||||
LAVENDER(MR.strings.theme_lavender),
|
||||
MIDNIGHT_DUSK(MR.strings.theme_midnightdusk),
|
||||
NORD(MR.strings.theme_nord),
|
||||
STRAWBERRY_DAIQUIRI(MR.strings.theme_strawberrydaiquiri),
|
||||
TAKO(MR.strings.theme_tako),
|
||||
TEALTURQUOISE(MR.strings.theme_tealturquoise),
|
||||
|
@ -53,6 +53,7 @@ import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsScreenModel
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
|
||||
@ -138,7 +139,7 @@ fun ExtensionDetailsScreen(
|
||||
private fun ExtensionDetails(
|
||||
contentPadding: PaddingValues,
|
||||
extension: Extension.Installed,
|
||||
sources: List<ExtensionSourceItem>,
|
||||
sources: ImmutableList<ExtensionSourceItem>,
|
||||
onClickSourcePreferences: (sourceId: Long) -> Unit,
|
||||
onClickUninstall: () -> Unit,
|
||||
onClickSource: (sourceId: Long) -> Unit,
|
||||
@ -150,18 +151,24 @@ private fun ExtensionDetails(
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
when {
|
||||
extension.isRepoSource ->
|
||||
extension.isFromExternalRepo ->
|
||||
item {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val url = remember(extension) {
|
||||
val regex = """https://raw.githubusercontent.com/(.+?)/(.+?)/.+""".toRegex()
|
||||
regex.find(extension.repoUrl.orEmpty())
|
||||
?.let {
|
||||
val (user, repo) = it.destructured
|
||||
"https://github.com/$user/$repo"
|
||||
}
|
||||
?: extension.repoUrl
|
||||
}
|
||||
|
||||
WarningBanner(
|
||||
MR.strings.repo_extension_message,
|
||||
modifier = Modifier.clickable {
|
||||
extension.repoUrl ?: return@clickable
|
||||
uriHandler.openUri(
|
||||
extension.repoUrl
|
||||
.replace("https://raw.githubusercontent.com", "https://github.com")
|
||||
.removeSuffix("/repo/"),
|
||||
)
|
||||
url ?: return@clickable
|
||||
uriHandler.openUri(url)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -74,6 +74,8 @@ private fun ColumnScope.FilterPage(
|
||||
) {
|
||||
val filterDownloaded by screenModel.libraryPreferences.filterDownloaded().collectAsState()
|
||||
val downloadedOnly by screenModel.preferences.downloadedOnly().collectAsState()
|
||||
val autoUpdateMangaRestrictions by screenModel.libraryPreferences.autoUpdateMangaRestrictions().collectAsState()
|
||||
|
||||
TriStateItem(
|
||||
label = stringResource(MR.strings.label_downloaded),
|
||||
state = if (downloadedOnly) {
|
||||
@ -108,6 +110,14 @@ private fun ColumnScope.FilterPage(
|
||||
state = filterCompleted,
|
||||
onClick = { screenModel.toggleFilter(LibraryPreferences::filterCompleted) },
|
||||
)
|
||||
if (LibraryPreferences.MANGA_OUTSIDE_RELEASE_PERIOD in autoUpdateMangaRestrictions) {
|
||||
val filterIntervalCustom by screenModel.libraryPreferences.filterIntervalCustom().collectAsState()
|
||||
TriStateItem(
|
||||
label = stringResource(MR.strings.action_filter_interval_custom),
|
||||
state = filterIntervalCustom,
|
||||
onClick = { screenModel.toggleFilter(LibraryPreferences::filterIntervalCustom) },
|
||||
)
|
||||
}
|
||||
|
||||
val trackers = remember { screenModel.trackers }
|
||||
when (trackers.size) {
|
||||
|
@ -30,6 +30,7 @@ import tachiyomi.presentation.core.i18n.pluralStringResource
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import java.time.Instant
|
||||
import java.time.temporal.ChronoUnit
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
@Composable
|
||||
fun DeleteChaptersDialog(
|
||||
@ -85,7 +86,7 @@ fun SetIntervalDialog(
|
||||
title = { Text(stringResource(MR.strings.pref_library_update_smart_update)) },
|
||||
text = {
|
||||
Column {
|
||||
if (nextUpdateDays != null && nextUpdateDays >= 0) {
|
||||
if (nextUpdateDays != null && nextUpdateDays >= 0 && interval >= 0) {
|
||||
Text(
|
||||
stringResource(
|
||||
MR.strings.manga_interval_expected_update,
|
||||
@ -96,8 +97,8 @@ fun SetIntervalDialog(
|
||||
),
|
||||
pluralStringResource(
|
||||
MR.plurals.day,
|
||||
count = interval,
|
||||
interval,
|
||||
count = interval.absoluteValue,
|
||||
interval.absoluteValue,
|
||||
),
|
||||
),
|
||||
)
|
||||
@ -105,7 +106,6 @@ fun SetIntervalDialog(
|
||||
Spacer(Modifier.height(MaterialTheme.padding.small))
|
||||
}
|
||||
|
||||
// TODO: selecting "1" then doesn't allow for future changes unless defaulting first?
|
||||
if (onValueChanged != null && (isDevFlavor || isPreviewBuildType)) {
|
||||
Text(stringResource(MR.strings.manga_interval_custom_amount))
|
||||
|
||||
|
@ -201,14 +201,14 @@ fun MangaActionRow(
|
||||
onLongClick = onEditCategory,
|
||||
)
|
||||
MangaActionButton(
|
||||
title = if (nextUpdateDays != null) {
|
||||
pluralStringResource(
|
||||
title = when (nextUpdateDays) {
|
||||
null -> stringResource(MR.strings.not_applicable)
|
||||
0 -> stringResource(MR.strings.manga_interval_expected_update_soon)
|
||||
else -> pluralStringResource(
|
||||
MR.plurals.day,
|
||||
count = nextUpdateDays,
|
||||
nextUpdateDays,
|
||||
)
|
||||
} else {
|
||||
stringResource(MR.strings.not_applicable)
|
||||
},
|
||||
icon = Icons.Default.HourglassEmpty,
|
||||
color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor,
|
||||
|
@ -24,6 +24,7 @@ import androidx.core.net.toUri
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.domain.source.interactor.TrustExtension
|
||||
import eu.kanade.presentation.more.settings.Preference
|
||||
import eu.kanade.presentation.more.settings.screen.advanced.ClearDatabaseScreen
|
||||
import eu.kanade.presentation.more.settings.screen.debug.DebugInfoScreen
|
||||
@ -340,6 +341,7 @@ object SettingsAdvancedScreen : SearchableSettings {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val extensionInstallerPref = basePreferences.extensionInstaller()
|
||||
var shizukuMissing by rememberSaveable { mutableStateOf(false) }
|
||||
val trustExtension = remember { Injekt.get<TrustExtension>() }
|
||||
|
||||
if (shizukuMissing) {
|
||||
val dismiss = { shizukuMissing = false }
|
||||
@ -392,6 +394,13 @@ object SettingsAdvancedScreen : SearchableSettings {
|
||||
}
|
||||
},
|
||||
),
|
||||
Preference.PreferenceItem.TextPreference(
|
||||
title = stringResource(MR.strings.ext_revoke_trust),
|
||||
onClick = {
|
||||
trustExtension.revokeAll()
|
||||
context.toast(MR.strings.requires_app_restart)
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import eu.kanade.presentation.theme.colorscheme.GreenAppleColorScheme
|
||||
import eu.kanade.presentation.theme.colorscheme.LavenderColorScheme
|
||||
import eu.kanade.presentation.theme.colorscheme.MidnightDuskColorScheme
|
||||
import eu.kanade.presentation.theme.colorscheme.MonetColorScheme
|
||||
import eu.kanade.presentation.theme.colorscheme.NordColorScheme
|
||||
import eu.kanade.presentation.theme.colorscheme.StrawberryColorScheme
|
||||
import eu.kanade.presentation.theme.colorscheme.TachiyomiColorScheme
|
||||
import eu.kanade.presentation.theme.colorscheme.TakoColorScheme
|
||||
@ -47,6 +48,7 @@ private fun getThemeColorScheme(
|
||||
AppTheme.GREEN_APPLE -> GreenAppleColorScheme
|
||||
AppTheme.LAVENDER -> LavenderColorScheme
|
||||
AppTheme.MIDNIGHT_DUSK -> MidnightDuskColorScheme
|
||||
AppTheme.NORD -> NordColorScheme
|
||||
AppTheme.STRAWBERRY_DAIQUIRI -> StrawberryColorScheme
|
||||
AppTheme.TAKO -> TakoColorScheme
|
||||
AppTheme.TEALTURQUOISE -> TealTurqoiseColorScheme
|
||||
|
@ -0,0 +1,72 @@
|
||||
package eu.kanade.presentation.theme.colorscheme
|
||||
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
/**
|
||||
* Colors for Nord theme
|
||||
* https://www.nordtheme.com/docs/colors-and-palettes
|
||||
* for the light theme, the primary color is switched with the tertiary for better contrast in some case
|
||||
*/
|
||||
internal object NordColorScheme : BaseColorScheme() {
|
||||
|
||||
override val darkScheme = darkColorScheme(
|
||||
primary = Color(0xFF88C0D0),
|
||||
onPrimary = Color(0xFF2E3440),
|
||||
primaryContainer = Color(0xFF88C0D0),
|
||||
onPrimaryContainer = Color(0xFF2E3440),
|
||||
inversePrimary = Color(0xFF397E91),
|
||||
secondary = Color(0xFF81A1C1),
|
||||
onSecondary = Color(0xFF2E3440),
|
||||
secondaryContainer = Color(0xFF81A1C1),
|
||||
onSecondaryContainer = Color(0xFF2E3440),
|
||||
tertiary = Color(0xFF5E81AC),
|
||||
onTertiary = Color(0xFF000000),
|
||||
tertiaryContainer = Color(0xFF5E81AC),
|
||||
onTertiaryContainer = Color(0xFF000000),
|
||||
background = Color(0xFF2E3440),
|
||||
onBackground = Color(0xFFECEFF4),
|
||||
surface = Color(0xFF3B4252),
|
||||
onSurface = Color(0xFFECEFF4),
|
||||
surfaceVariant = Color(0xFF2E3440),
|
||||
onSurfaceVariant = Color(0xFFECEFF4),
|
||||
surfaceTint = Color(0xFF88C0D0),
|
||||
inverseSurface = Color(0xFFD8DEE9),
|
||||
inverseOnSurface = Color(0xFF2E3440),
|
||||
outline = Color(0xFF6d717b),
|
||||
outlineVariant = Color(0xFF90939a),
|
||||
onError = Color(0xFF2E3440),
|
||||
errorContainer = Color(0xFFBF616A),
|
||||
onErrorContainer = Color(0xFF000000),
|
||||
)
|
||||
|
||||
override val lightScheme = lightColorScheme(
|
||||
primary = Color(0xFF5E81AC),
|
||||
onPrimary = Color(0xFF000000),
|
||||
primaryContainer = Color(0xFF5E81AC),
|
||||
onPrimaryContainer = Color(0xFF000000),
|
||||
inversePrimary = Color(0xFF8CA8CD),
|
||||
secondary = Color(0xFF81A1C1),
|
||||
onSecondary = Color(0xFF2E3440),
|
||||
secondaryContainer = Color(0xFF81A1C1),
|
||||
onSecondaryContainer = Color(0xFF2E3440),
|
||||
tertiary = Color(0xFF88C0D0),
|
||||
onTertiary = Color(0xFF2E3440),
|
||||
tertiaryContainer = Color(0xFF88C0D0),
|
||||
onTertiaryContainer = Color(0xFF2E3440),
|
||||
background = Color(0xFFECEFF4),
|
||||
onBackground = Color(0xFF2E3440),
|
||||
surface = Color(0xFFE5E9F0),
|
||||
onSurface = Color(0xFF2E3440),
|
||||
surfaceVariant = Color(0xFFffffff),
|
||||
onSurfaceVariant = Color(0xFF2E3440),
|
||||
surfaceTint = Color(0xFF5E81AC),
|
||||
inverseSurface = Color(0xFF3B4252),
|
||||
inverseOnSurface = Color(0xFFECEFF4),
|
||||
outline = Color(0xFF2E3440),
|
||||
onError = Color(0xFFECEFF4),
|
||||
errorContainer = Color(0xFFBF616A),
|
||||
onErrorContainer = Color(0xFF000000),
|
||||
)
|
||||
}
|
@ -7,6 +7,7 @@ import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@ -22,7 +23,6 @@ import androidx.compose.foundation.layout.paddingFromBaseline
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
@ -33,6 +33,7 @@ import androidx.compose.material.icons.filled.CheckCircle
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
@ -40,7 +41,10 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
@ -48,7 +52,11 @@ import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.platform.ClipboardManager
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.capitalize
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
@ -58,9 +66,11 @@ import androidx.compose.ui.text.toLowerCase
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.components.DropdownMenu
|
||||
import eu.kanade.presentation.manga.components.MangaCover
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
@ -188,13 +198,7 @@ fun TrackerSearch(
|
||||
key = { it.hashCode() },
|
||||
) {
|
||||
SearchResultItem(
|
||||
title = it.title,
|
||||
coverUrl = it.cover_url,
|
||||
type = it.publishing_type.toLowerCase(Locale.current).capitalize(Locale.current),
|
||||
startDate = it.start_date,
|
||||
status = it.publishing_status.toLowerCase(Locale.current).capitalize(Locale.current),
|
||||
score = it.score,
|
||||
description = it.summary.trim(),
|
||||
trackSearch = it,
|
||||
selected = it == selected,
|
||||
onClick = { onSelectedChange(it) },
|
||||
)
|
||||
@ -214,18 +218,18 @@ fun TrackerSearch(
|
||||
|
||||
@Composable
|
||||
private fun SearchResultItem(
|
||||
title: String,
|
||||
coverUrl: String,
|
||||
type: String,
|
||||
startDate: String,
|
||||
status: String,
|
||||
score: Float,
|
||||
description: String,
|
||||
trackSearch: TrackSearch,
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val clipboardManager: ClipboardManager = LocalClipboardManager.current
|
||||
val type = trackSearch.publishing_type.toLowerCase(Locale.current).capitalize(Locale.current)
|
||||
val status = trackSearch.publishing_status.toLowerCase(Locale.current).capitalize(Locale.current)
|
||||
val description = trackSearch.summary.trim()
|
||||
val shape = RoundedCornerShape(16.dp)
|
||||
val borderColor = if (selected) MaterialTheme.colorScheme.outline else Color.Transparent
|
||||
var dropDownMenuExpanded by remember { mutableStateOf(false) }
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@ -237,7 +241,10 @@ private fun SearchResultItem(
|
||||
color = borderColor,
|
||||
shape = shape,
|
||||
)
|
||||
.selectable(selected = selected, onClick = onClick)
|
||||
.combinedClickable(
|
||||
onLongClick = { dropDownMenuExpanded = true },
|
||||
onClick = onClick,
|
||||
)
|
||||
.padding(12.dp),
|
||||
) {
|
||||
if (selected) {
|
||||
@ -251,28 +258,41 @@ private fun SearchResultItem(
|
||||
Column {
|
||||
Row {
|
||||
MangaCover.Book(
|
||||
data = coverUrl,
|
||||
data = trackSearch.cover_url,
|
||||
modifier = Modifier.height(96.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Column {
|
||||
Text(
|
||||
text = title,
|
||||
text = trackSearch.title,
|
||||
modifier = Modifier.padding(end = 28.dp),
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
SearchResultItemDropDownMenu(
|
||||
expanded = dropDownMenuExpanded,
|
||||
onCollapseMenu = { dropDownMenuExpanded = false },
|
||||
onCopyName = {
|
||||
clipboardManager.setText(AnnotatedString(trackSearch.title))
|
||||
},
|
||||
onOpenInBrowser = {
|
||||
val url = trackSearch.tracking_url
|
||||
if (url.isNotBlank()) {
|
||||
context.openInBrowser(url)
|
||||
}
|
||||
},
|
||||
)
|
||||
if (type.isNotBlank()) {
|
||||
SearchResultItemDetails(
|
||||
title = stringResource(MR.strings.track_type),
|
||||
text = type,
|
||||
)
|
||||
}
|
||||
if (startDate.isNotBlank()) {
|
||||
if (trackSearch.start_date.isNotBlank()) {
|
||||
SearchResultItemDetails(
|
||||
title = stringResource(MR.strings.label_started),
|
||||
text = startDate,
|
||||
text = trackSearch.start_date,
|
||||
)
|
||||
}
|
||||
if (status.isNotBlank()) {
|
||||
@ -281,10 +301,10 @@ private fun SearchResultItem(
|
||||
text = status,
|
||||
)
|
||||
}
|
||||
if (score != -1f) {
|
||||
if (trackSearch.score != -1f) {
|
||||
SearchResultItemDetails(
|
||||
title = stringResource(MR.strings.score),
|
||||
text = score.toString(),
|
||||
text = trackSearch.score.toString(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -304,6 +324,33 @@ private fun SearchResultItem(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SearchResultItemDropDownMenu(
|
||||
expanded: Boolean,
|
||||
onCollapseMenu: () -> Unit,
|
||||
onCopyName: () -> Unit,
|
||||
onOpenInBrowser: () -> Unit,
|
||||
) {
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = onCollapseMenu,
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(MR.strings.action_copy_to_clipboard)) },
|
||||
onClick = {
|
||||
onCopyName()
|
||||
onCollapseMenu()
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(MR.strings.action_open_in_browser)) },
|
||||
onClick = {
|
||||
onOpenInBrowser()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SearchResultItemDetails(
|
||||
title: String,
|
||||
|
@ -185,7 +185,17 @@ class ExtensionManager(
|
||||
val hasUpdate = installedExt.updateExists(availableExt)
|
||||
|
||||
if (installedExt.hasUpdate != hasUpdate) {
|
||||
mutInstalledExtensions[index] = installedExt.copy(hasUpdate = hasUpdate)
|
||||
mutInstalledExtensions[index] = installedExt.copy(
|
||||
hasUpdate = hasUpdate,
|
||||
isFromExternalRepo = availableExt.isFromExternalRepo,
|
||||
repoUrl = availableExt.repoUrl,
|
||||
)
|
||||
changed = true
|
||||
} else if (availableExt.isFromExternalRepo) {
|
||||
mutInstalledExtensions[index] = installedExt.copy(
|
||||
isFromExternalRepo = true,
|
||||
repoUrl = availableExt.repoUrl,
|
||||
)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ internal class ExtensionApi {
|
||||
apkName = it.apk,
|
||||
iconUrl = "$repoUrl/icon/${it.pkg}.png",
|
||||
repoUrl = repoUrl,
|
||||
isRepoSource = isRepoSource,
|
||||
isFromExternalRepo = isRepoSource,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ sealed class Extension {
|
||||
val isUnofficial: Boolean = false,
|
||||
val isShared: Boolean,
|
||||
val repoUrl: String? = null,
|
||||
val isRepoSource: Boolean = false,
|
||||
val isFromExternalRepo: Boolean = false,
|
||||
) : Extension()
|
||||
|
||||
data class Available(
|
||||
@ -45,7 +45,7 @@ sealed class Extension {
|
||||
val apkName: String,
|
||||
val iconUrl: String,
|
||||
val repoUrl: String,
|
||||
val isRepoSource: Boolean,
|
||||
val isFromExternalRepo: Boolean,
|
||||
) : Extension() {
|
||||
|
||||
data class Source(
|
||||
|
@ -26,6 +26,9 @@ interface ThemingDelegate {
|
||||
AppTheme.MIDNIGHT_DUSK -> {
|
||||
resIds += R.style.Theme_Tachiyomi_MidnightDusk
|
||||
}
|
||||
AppTheme.NORD -> {
|
||||
resIds += R.style.Theme_Tachiyomi_Nord
|
||||
}
|
||||
AppTheme.STRAWBERRY_DAIQUIRI -> {
|
||||
resIds += R.style.Theme_Tachiyomi_StrawberryDaiquiri
|
||||
}
|
||||
|
@ -157,6 +157,7 @@ class LibraryScreenModel(
|
||||
prefs.filterStarted,
|
||||
prefs.filterBookmarked,
|
||||
prefs.filterCompleted,
|
||||
prefs.filterIntervalCustom,
|
||||
) + trackFilter.values
|
||||
).any { it != TriState.DISABLED }
|
||||
}
|
||||
@ -178,12 +179,13 @@ class LibraryScreenModel(
|
||||
): LibraryMap {
|
||||
val prefs = getLibraryItemPreferencesFlow().first()
|
||||
val downloadedOnly = prefs.globalFilterDownloaded
|
||||
val filterDownloaded =
|
||||
if (downloadedOnly) TriState.ENABLED_IS else prefs.filterDownloaded
|
||||
val skipOutsideReleasePeriod = prefs.skipOutsideReleasePeriod
|
||||
val filterDownloaded = if (downloadedOnly) TriState.ENABLED_IS else prefs.filterDownloaded
|
||||
val filterUnread = prefs.filterUnread
|
||||
val filterStarted = prefs.filterStarted
|
||||
val filterBookmarked = prefs.filterBookmarked
|
||||
val filterCompleted = prefs.filterCompleted
|
||||
val filterIntervalCustom = prefs.filterIntervalCustom
|
||||
|
||||
val isNotLoggedInAnyTrack = loggedInTrackers.isEmpty()
|
||||
|
||||
@ -215,6 +217,14 @@ class LibraryScreenModel(
|
||||
applyFilter(filterCompleted) { it.libraryManga.manga.status.toInt() == SManga.COMPLETED }
|
||||
}
|
||||
|
||||
val filterFnIntervalCustom: (LibraryItem) -> Boolean = {
|
||||
if (skipOutsideReleasePeriod) {
|
||||
applyFilter(filterIntervalCustom) { it.libraryManga.manga.fetchInterval < 0 }
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
val filterFnTracking: (LibraryItem) -> Boolean = tracking@{ item ->
|
||||
if (isNotLoggedInAnyTrack || trackFiltersIsIgnored) return@tracking true
|
||||
|
||||
@ -225,7 +235,7 @@ class LibraryScreenModel(
|
||||
val isExcluded = excludedTracks.isNotEmpty() && mangaTracks.fastAny { it in excludedTracks }
|
||||
val isIncluded = includedTracks.isEmpty() || mangaTracks.fastAny { it in includedTracks }
|
||||
|
||||
return@tracking !isExcluded && isIncluded
|
||||
!isExcluded && isIncluded
|
||||
}
|
||||
|
||||
val filterFn: (LibraryItem) -> Boolean = {
|
||||
@ -234,6 +244,7 @@ class LibraryScreenModel(
|
||||
filterFnStarted(it) &&
|
||||
filterFnBookmarked(it) &&
|
||||
filterFnCompleted(it) &&
|
||||
filterFnIntervalCustom(it) &&
|
||||
filterFnTracking(it)
|
||||
}
|
||||
|
||||
@ -320,6 +331,7 @@ class LibraryScreenModel(
|
||||
libraryPreferences.downloadBadge().changes(),
|
||||
libraryPreferences.localBadge().changes(),
|
||||
libraryPreferences.languageBadge().changes(),
|
||||
libraryPreferences.autoUpdateMangaRestrictions().changes(),
|
||||
|
||||
preferences.downloadedOnly().changes(),
|
||||
libraryPreferences.filterDownloaded().changes(),
|
||||
@ -327,20 +339,22 @@ class LibraryScreenModel(
|
||||
libraryPreferences.filterStarted().changes(),
|
||||
libraryPreferences.filterBookmarked().changes(),
|
||||
libraryPreferences.filterCompleted().changes(),
|
||||
transform = {
|
||||
ItemPreferences(
|
||||
downloadBadge = it[0] as Boolean,
|
||||
localBadge = it[1] as Boolean,
|
||||
languageBadge = it[2] as Boolean,
|
||||
globalFilterDownloaded = it[3] as Boolean,
|
||||
filterDownloaded = it[4] as TriState,
|
||||
filterUnread = it[5] as TriState,
|
||||
filterStarted = it[6] as TriState,
|
||||
filterBookmarked = it[7] as TriState,
|
||||
filterCompleted = it[8] as TriState,
|
||||
)
|
||||
},
|
||||
)
|
||||
libraryPreferences.filterIntervalCustom().changes(),
|
||||
) {
|
||||
ItemPreferences(
|
||||
downloadBadge = it[0] as Boolean,
|
||||
localBadge = it[1] as Boolean,
|
||||
languageBadge = it[2] as Boolean,
|
||||
skipOutsideReleasePeriod = LibraryPreferences.MANGA_OUTSIDE_RELEASE_PERIOD in (it[3] as Set<*>),
|
||||
globalFilterDownloaded = it[4] as Boolean,
|
||||
filterDownloaded = it[5] as TriState,
|
||||
filterUnread = it[6] as TriState,
|
||||
filterStarted = it[7] as TriState,
|
||||
filterBookmarked = it[8] as TriState,
|
||||
filterCompleted = it[9] as TriState,
|
||||
filterIntervalCustom = it[10] as TriState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -699,6 +713,7 @@ class LibraryScreenModel(
|
||||
val downloadBadge: Boolean,
|
||||
val localBadge: Boolean,
|
||||
val languageBadge: Boolean,
|
||||
val skipOutsideReleasePeriod: Boolean,
|
||||
|
||||
val globalFilterDownloaded: Boolean,
|
||||
val filterDownloaded: TriState,
|
||||
@ -706,6 +721,7 @@ class LibraryScreenModel(
|
||||
val filterStarted: TriState,
|
||||
val filterBookmarked: TriState,
|
||||
val filterCompleted: TriState,
|
||||
val filterIntervalCustom: TriState,
|
||||
)
|
||||
|
||||
@Immutable
|
||||
|
@ -378,12 +378,15 @@ class MangaScreenModel(
|
||||
|
||||
fun setFetchInterval(manga: Manga, interval: Int) {
|
||||
screenModelScope.launchIO {
|
||||
updateManga.awaitUpdateFetchInterval(
|
||||
// Custom intervals are negative
|
||||
manga.copy(fetchInterval = -interval),
|
||||
)
|
||||
val updatedManga = mangaRepository.getMangaById(manga.id)
|
||||
updateSuccessState { it.copy(manga = updatedManga) }
|
||||
if (
|
||||
updateManga.awaitUpdateFetchInterval(
|
||||
// Custom intervals are negative
|
||||
manga.copy(fetchInterval = -interval),
|
||||
)
|
||||
) {
|
||||
val updatedManga = mangaRepository.getMangaById(manga.id)
|
||||
updateSuccessState { it.copy(manga = updatedManga) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -462,7 +462,8 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||
manga.title,
|
||||
manga.source,
|
||||
)
|
||||
if (isNextChapterDownloaded) return@launchIO
|
||||
if (!isNextChapterDownloaded) return@launchIO
|
||||
|
||||
val chaptersToDownload = getNextChapters.await(manga.id, nextChapter.id!!).run {
|
||||
if (readerPreferences.skipDupe().get()) {
|
||||
removeDuplicates(nextChapter.toDomainChapter()!!)
|
||||
|
@ -214,6 +214,9 @@ class WebtoonRecyclerView @JvmOverloads constructor(
|
||||
if (!isZooming && doubleTapZoom) {
|
||||
if (scaleX != DEFAULT_RATE) {
|
||||
zoom(currentScale, DEFAULT_RATE, x, 0f, y, 0f)
|
||||
layoutParams.height = originalHeight
|
||||
halfHeight = layoutParams.height / 2
|
||||
requestLayout()
|
||||
} else {
|
||||
val toScale = 2f
|
||||
val toX = (halfWidth - ev.x) * (toScale - 1)
|
||||
|
@ -333,6 +333,37 @@
|
||||
<item name="colorPrimaryInverse">@color/tidalwave_primaryInverse</item>
|
||||
</style>
|
||||
|
||||
<!--== Nord Theme ==-->
|
||||
<style name="Theme.Tachiyomi.Nord">
|
||||
<!-- Theme Colors -->
|
||||
<item name="colorPrimary">@color/nord_primary</item>
|
||||
<item name="colorOnPrimary">@color/nord_onPrimary</item>
|
||||
<item name="colorPrimaryContainer">@color/nord_primaryContainer</item>
|
||||
<item name="colorOnPrimaryContainer">@color/nord_onPrimaryContainer</item>
|
||||
<item name="colorSecondary">@color/nord_secondary</item>
|
||||
<item name="colorOnSecondary">@color/nord_onSecondary</item>
|
||||
<item name="colorSecondaryContainer">@color/nord_secondaryContainer</item>
|
||||
<item name="colorOnSecondaryContainer">@color/nord_onSecondaryContainer</item>
|
||||
<item name="colorTertiary">@color/nord_tertiary</item>
|
||||
<item name="colorOnTertiary">@color/nord_onTertiary</item>
|
||||
<item name="colorTertiaryContainer">@color/nord_tertiaryContainer</item>
|
||||
<item name="colorOnTertiaryContainer">@color/nord_onTertiaryContainer</item>
|
||||
<item name="android:colorBackground">@color/nord_background</item>
|
||||
<item name="colorOnBackground">@color/nord_onBackground</item>
|
||||
<item name="colorSurface">@color/nord_surface</item>
|
||||
<item name="colorOnSurface">@color/nord_onSurface</item>
|
||||
<item name="colorSurfaceVariant">@color/nord_surfaceVariant</item>
|
||||
<item name="colorOnSurfaceVariant">@color/nord_onSurfaceVariant</item>
|
||||
<item name="colorOutline">@color/nord_outline</item>
|
||||
<item name="colorOnSurfaceInverse">@color/nord_inverseOnSurface</item>
|
||||
<item name="colorSurfaceInverse">@color/nord_inverseSurface</item>
|
||||
<item name="colorPrimaryInverse">@color/nord_primaryInverse</item>
|
||||
<item name="colorOnError">@color/nord_onError</item>
|
||||
<item name="colorErrorContainer">@color/nord_errorContainer</item>
|
||||
<item name="colorOnErrorContainer">@color/nord_onErrorContainer</item>
|
||||
<item name="elevationOverlayColor">@color/nord_elevationOverlay</item>
|
||||
</style>
|
||||
|
||||
<!--== AMOLED Mode Overlay ==-->
|
||||
<style name="ThemeOverlay.Tachiyomi.Amoled" parent="" />
|
||||
|
||||
|
@ -85,26 +85,6 @@ class LibraryPreferences(
|
||||
TriState.DISABLED,
|
||||
)
|
||||
|
||||
fun filterIntervalLong() = preferenceStore.getEnum(
|
||||
"pref_filter_library_interval_long",
|
||||
TriState.DISABLED,
|
||||
)
|
||||
|
||||
fun filterIntervalLate() = preferenceStore.getEnum(
|
||||
"pref_filter_library_interval_late",
|
||||
TriState.DISABLED,
|
||||
)
|
||||
|
||||
fun filterIntervalDropped() = preferenceStore.getEnum(
|
||||
"pref_filter_library_interval_dropped",
|
||||
TriState.DISABLED,
|
||||
)
|
||||
|
||||
fun filterIntervalPassed() = preferenceStore.getEnum(
|
||||
"pref_filter_library_interval_passed",
|
||||
TriState.DISABLED,
|
||||
)
|
||||
|
||||
fun filterTracking(id: Int) = preferenceStore.getEnum(
|
||||
"pref_filter_library_tracked_${id}_v2",
|
||||
TriState.DISABLED,
|
||||
|
@ -14,11 +14,11 @@ class FetchInterval(
|
||||
private val getChaptersByMangaId: GetChaptersByMangaId,
|
||||
) {
|
||||
|
||||
suspend fun toMangaUpdateOrNull(
|
||||
suspend fun toMangaUpdate(
|
||||
manga: Manga,
|
||||
dateTime: ZonedDateTime,
|
||||
window: Pair<Long, Long>,
|
||||
): MangaUpdate? {
|
||||
): MangaUpdate {
|
||||
val interval = manga.fetchInterval.takeIf { it < 0 } ?: calculateInterval(
|
||||
chapters = getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true),
|
||||
zone = dateTime.zone,
|
||||
@ -30,11 +30,7 @@ class FetchInterval(
|
||||
}
|
||||
val nextUpdate = calculateNextUpdate(manga, interval, dateTime, currentWindow)
|
||||
|
||||
return if (manga.nextUpdate == nextUpdate && manga.fetchInterval == interval) {
|
||||
null
|
||||
} else {
|
||||
MangaUpdate(id = manga.id, nextUpdate = nextUpdate, fetchInterval = interval)
|
||||
}
|
||||
return MangaUpdate(id = manga.id, nextUpdate = nextUpdate, fetchInterval = interval)
|
||||
}
|
||||
|
||||
fun getWindow(dateTime: ZonedDateTime): Pair<Long, Long> {
|
||||
@ -96,34 +92,31 @@ class FetchInterval(
|
||||
dateTime: ZonedDateTime,
|
||||
window: Pair<Long, Long>,
|
||||
): Long {
|
||||
return if (
|
||||
manga.nextUpdate !in window.first.rangeTo(window.second + 1) ||
|
||||
manga.fetchInterval == 0
|
||||
) {
|
||||
val latestDate = ZonedDateTime.ofInstant(
|
||||
if (manga.lastUpdate > 0) Instant.ofEpochMilli(manga.lastUpdate) else Instant.now(),
|
||||
dateTime.zone,
|
||||
)
|
||||
.toLocalDate()
|
||||
.atStartOfDay()
|
||||
val timeSinceLatest = ChronoUnit.DAYS.between(latestDate, dateTime).toInt()
|
||||
val cycle = timeSinceLatest.floorDiv(
|
||||
interval.absoluteValue.takeIf { interval < 0 }
|
||||
?: doubleInterval(interval, timeSinceLatest, doubleWhenOver = 10),
|
||||
)
|
||||
latestDate.plusDays((cycle + 1) * interval.toLong()).toEpochSecond(dateTime.offset) * 1000
|
||||
} else {
|
||||
manga.nextUpdate
|
||||
if (manga.nextUpdate in window.first.rangeTo(window.second + 1)) {
|
||||
return manga.nextUpdate
|
||||
}
|
||||
|
||||
val latestDate = ZonedDateTime.ofInstant(
|
||||
if (manga.lastUpdate > 0) Instant.ofEpochMilli(manga.lastUpdate) else Instant.now(),
|
||||
dateTime.zone,
|
||||
)
|
||||
.toLocalDate()
|
||||
.atStartOfDay()
|
||||
val timeSinceLatest = ChronoUnit.DAYS.between(latestDate, dateTime).toInt()
|
||||
val cycle = timeSinceLatest.floorDiv(
|
||||
interval.absoluteValue.takeIf { interval < 0 }
|
||||
?: increaseInterval(interval, timeSinceLatest, increaseWhenOver = 10),
|
||||
)
|
||||
return latestDate.plusDays((cycle + 1) * interval.toLong()).toEpochSecond(dateTime.offset) * 1000
|
||||
}
|
||||
|
||||
private fun doubleInterval(delta: Int, timeSinceLatest: Int, doubleWhenOver: Int): Int {
|
||||
private fun increaseInterval(delta: Int, timeSinceLatest: Int, increaseWhenOver: Int): Int {
|
||||
if (delta >= MAX_INTERVAL) return MAX_INTERVAL
|
||||
|
||||
// double delta again if missed more than 9 check in new delta
|
||||
val cycle = timeSinceLatest.floorDiv(delta) + 1
|
||||
return if (cycle > doubleWhenOver) {
|
||||
doubleInterval(delta * 2, timeSinceLatest, doubleWhenOver)
|
||||
return if (cycle > increaseWhenOver) {
|
||||
increaseInterval(delta * 2, timeSinceLatest, increaseWhenOver)
|
||||
} else {
|
||||
delta
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ sqldelight-gradle = { module = "app.cash.sqldelight:gradle-plugin", version.ref
|
||||
|
||||
junit = "org.junit.jupiter:junit-jupiter:5.10.1"
|
||||
kotest-assertions = "io.kotest:kotest-assertions-core:5.8.0"
|
||||
mockk = "io.mockk:mockk:1.13.8"
|
||||
mockk = "io.mockk:mockk:1.13.9"
|
||||
|
||||
voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" }
|
||||
voyager-screenmodel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyager" }
|
||||
|
@ -55,11 +55,7 @@
|
||||
<string name="action_filter_bookmarked">Bookmarked</string>
|
||||
<string name="action_filter_tracked">Tracked</string>
|
||||
<string name="action_filter_unread">Unread</string>
|
||||
<string name="action_filter_interval_custom">Customized fetch interval</string>
|
||||
<string name="action_filter_interval_long">Fetch monthly (28 days)</string>
|
||||
<string name="action_filter_interval_late">Late 10+ check</string>
|
||||
<string name="action_filter_interval_dropped">Dropped? Late 20+ and 2 months</string>
|
||||
<string name="action_filter_interval_passed">Passed check period</string>
|
||||
<string name="action_filter_interval_custom">Customized update frequency</string>
|
||||
<!-- reserved for #4048 -->
|
||||
<string name="action_filter_empty">Remove filter</string>
|
||||
<string name="action_sort_alpha">Alphabetically</string>
|
||||
@ -227,6 +223,7 @@
|
||||
<string name="theme_greenapple">Green Apple</string>
|
||||
<string name="theme_lavender">Lavender</string>
|
||||
<string name="theme_midnightdusk">Midnight Dusk</string>
|
||||
<string name="theme_nord">Nord</string>
|
||||
<string name="theme_strawberrydaiquiri">Strawberry Daiquiri</string>
|
||||
<string name="theme_tako">Tako</string>
|
||||
<string name="theme_tealturquoise">Teal & Turquoise</string>
|
||||
@ -326,8 +323,8 @@
|
||||
<string name="untrusted_extension">Untrusted extension</string>
|
||||
<string name="untrusted_extension_message">This extension was signed by any unknown author and wasn\'t loaded.\n\nMalicious extensions can read any stored login credentials or execute arbitrary code.\n\nBy trusting this extension\'s certificate, you accept these risks.</string>
|
||||
<string name="obsolete_extension_message">This extension is no longer available. It may not function properly and can cause issues with the app. Uninstalling it is recommended.</string>
|
||||
<string name="unofficial_extension_message">This extension is not from the official list.</string>
|
||||
<string name="extension_api_error">Failed to get extensions list</string>
|
||||
<string name="unofficial_extension_message">This extension is not from the official repo.</string>
|
||||
<string name="extension_api_error">Failed to fetch available extensions</string>
|
||||
<string name="ext_info_version">Version</string>
|
||||
<string name="ext_info_language">Language</string>
|
||||
<string name="ext_info_age_rating">Age rating</string>
|
||||
@ -342,6 +339,7 @@
|
||||
<string name="ext_installer_private" translatable="false">Private</string>
|
||||
<string name="ext_installer_shizuku_stopped">Shizuku is not running</string>
|
||||
<string name="ext_installer_shizuku_unavailable_dialog">Install and start Shizuku to use Shizuku as extension installer.</string>
|
||||
<string name="ext_revoke_trust">Revoke trusted unknown extensions</string>
|
||||
|
||||
<!-- Extension repos -->
|
||||
<string name="label_extension_repos">Extension repos</string>
|
||||
@ -722,9 +720,9 @@
|
||||
<string name="display_mode_chapter">Chapter %1$s</string>
|
||||
<string name="manga_display_interval_title">Estimate every</string>
|
||||
<string name="manga_display_modified_interval_title">Set to update every</string>
|
||||
<string name="manga_interval_header">Next update</string>
|
||||
<!-- "... around 2 days" -->
|
||||
<string name="manga_interval_expected_update">Next update expected in around %1$s, checking around every %2$s</string>
|
||||
<string name="manga_interval_expected_update">New chapters predicted to be released in around %1$s, checking around every %2$s.</string>
|
||||
<string name="manga_interval_expected_update_soon">Soon</string>
|
||||
<string name="manga_interval_custom_amount">Custom update frequency:</string>
|
||||
<string name="chapter_downloading_progress">Downloading (%1$d/%2$d)</string>
|
||||
<string name="chapter_error">Error</string>
|
||||
|
30
presentation-core/src/main/res/values-night/colors_nord.xml
Normal file
30
presentation-core/src/main/res/values-night/colors_nord.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--https://www.nordtheme.com/docs/colors-and-palettes-->
|
||||
<resources>
|
||||
<color name="nord_primary">#88C0D0</color>
|
||||
<color name="nord_onPrimary">#2E3440</color>
|
||||
<color name="nord_primaryContainer">#88C0D0</color>
|
||||
<color name="nord_onPrimaryContainer">#2E3440</color>
|
||||
<color name="nord_secondary">#81A1C1</color>
|
||||
<color name="nord_onSecondary">#2E3440</color>
|
||||
<color name="nord_secondaryContainer">#81A1C1</color>
|
||||
<color name="nord_onSecondaryContainer">#2E3440</color>
|
||||
<color name="nord_tertiary">#5E81AC</color>
|
||||
<color name="nord_onTertiary">#000000</color>
|
||||
<color name="nord_tertiaryContainer">#5E81AC</color>
|
||||
<color name="nord_onTertiaryContainer">#000000</color>
|
||||
<color name="nord_background">#2E3440</color>
|
||||
<color name="nord_onBackground">#ECEFF4</color>
|
||||
<color name="nord_surface">#3B4252</color>
|
||||
<color name="nord_onSurface">#ECEFF4</color>
|
||||
<color name="nord_surfaceVariant">#2E3440</color>
|
||||
<color name="nord_onSurfaceVariant">#ECEFF4</color>
|
||||
<color name="nord_outline">#D8DEE9</color>
|
||||
<color name="nord_inverseOnSurface">#2E3440</color>
|
||||
<color name="nord_inverseSurface">#D8DEE9</color>
|
||||
<color name="nord_primaryInverse">#397E91</color>
|
||||
<color name="nord_elevationOverlay">#434C5E</color>
|
||||
<color name="nord_onError">#2E3440</color>
|
||||
<color name="nord_errorContainer">#BF616A</color>
|
||||
<color name="nord_onErrorContainer">#000000</color>
|
||||
</resources>
|
31
presentation-core/src/main/res/values/colors_nord.xml
Normal file
31
presentation-core/src/main/res/values/colors_nord.xml
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--https://www.nordtheme.com/docs/colors-and-palettes-->
|
||||
<!--for the light theme, the primary color is switched with the tertiary for better contrast in some case-->
|
||||
<resources>
|
||||
<color name="nord_primary">#5E81AC</color>
|
||||
<color name="nord_onPrimary">#000000</color>
|
||||
<color name="nord_primaryContainer">#5E81AC</color>
|
||||
<color name="nord_onPrimaryContainer">#000000</color>
|
||||
<color name="nord_secondary">#81A1C1</color>
|
||||
<color name="nord_onSecondary">#2E3440</color>
|
||||
<color name="nord_secondaryContainer">#81A1C1</color>
|
||||
<color name="nord_onSecondaryContainer">#2E3440</color>
|
||||
<color name="nord_tertiary">#88C0D0</color>
|
||||
<color name="nord_onTertiary">#2E3440</color>
|
||||
<color name="nord_tertiaryContainer">#88C0D0</color>
|
||||
<color name="nord_onTertiaryContainer">#2E3440</color>
|
||||
<color name="nord_background">#ECEFF4</color>
|
||||
<color name="nord_onBackground">#2E3440</color>
|
||||
<color name="nord_surface">#E5E9F0</color>
|
||||
<color name="nord_onSurface">#2E3440</color>
|
||||
<color name="nord_surfaceVariant">#ffffff</color>
|
||||
<color name="nord_onSurfaceVariant">#2E3440</color>
|
||||
<color name="nord_outline">#4C566A</color>
|
||||
<color name="nord_inverseOnSurface">#ECEFF4</color>
|
||||
<color name="nord_inverseSurface">#3B4252</color>
|
||||
<color name="nord_primaryInverse">#8CA8CD</color>
|
||||
<color name="nord_elevationOverlay">#D8DEE9</color>
|
||||
<color name="nord_onError">#ECEFF4</color>
|
||||
<color name="nord_errorContainer">#BF616A</color>
|
||||
<color name="nord_onErrorContainer">#000000</color>
|
||||
</resources>
|
Loading…
Reference in New Issue
Block a user