mirror of
https://github.com/aniyomiorg/aniyomi.git
synced 2024-11-22 21:02:16 +03:00
FIX IT ALL AAAAAAAAAAAAAAA
This commit is contained in:
parent
d76e5b2a81
commit
5e8c406f7a
163 changed files with 2654 additions and 3423 deletions
|
@ -15,7 +15,7 @@ class GetLanguagesWithAnimeSources(
|
||||||
fun subscribe(): Flow<Map<String, List<AnimeSource>>> {
|
fun subscribe(): Flow<Map<String, List<AnimeSource>>> {
|
||||||
return combine(
|
return combine(
|
||||||
preferences.enabledLanguages().changes(),
|
preferences.enabledLanguages().changes(),
|
||||||
preferences.disabledSources().changes(),
|
preferences.disabledAnimeSources().changes(),
|
||||||
repository.getOnlineSources(),
|
repository.getOnlineSources(),
|
||||||
) { enabledLanguage, disabledSource, onlineSources ->
|
) { enabledLanguage, disabledSource, onlineSources ->
|
||||||
val sortedSources = onlineSources.sortedWith(
|
val sortedSources = onlineSources.sortedWith(
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package eu.kanade.domain.episode.model
|
package eu.kanade.domain.episode.model
|
||||||
|
|
||||||
import eu.kanade.domain.anime.model.Anime
|
import eu.kanade.domain.anime.model.Anime
|
||||||
import eu.kanade.domain.manga.model.TriStateFilter
|
|
||||||
import eu.kanade.domain.anime.model.isLocal
|
import eu.kanade.domain.anime.model.isLocal
|
||||||
|
import eu.kanade.domain.manga.model.TriStateFilter
|
||||||
import eu.kanade.tachiyomi.data.animedownload.AnimeDownloadManager
|
import eu.kanade.tachiyomi.data.animedownload.AnimeDownloadManager
|
||||||
import eu.kanade.tachiyomi.data.animedownload.model.AnimeDownload
|
import eu.kanade.tachiyomi.data.animedownload.model.AnimeDownload
|
||||||
import eu.kanade.tachiyomi.ui.anime.EpisodeItem
|
import eu.kanade.tachiyomi.ui.anime.EpisodeItem
|
||||||
|
|
|
@ -50,6 +50,18 @@ class LibraryPreferences(
|
||||||
|
|
||||||
fun filterTracking(name: Int) = preferenceStore.getInt("pref_filter_library_tracked_$name", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
|
fun filterTracking(name: Int) = preferenceStore.getInt("pref_filter_library_tracked_$name", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
|
||||||
|
|
||||||
|
fun filterDownloadedAnime() = preferenceStore.getInt("pref_filter_animelib_downloaded", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
|
||||||
|
|
||||||
|
fun filterUnseen() = preferenceStore.getInt("pref_filter_animelib_unread", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
|
||||||
|
|
||||||
|
fun filterStartedAnime() = preferenceStore.getInt("pref_filter_animelib_started", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
|
||||||
|
|
||||||
|
fun filterBookmarkedAnime() = preferenceStore.getInt("pref_filter_animelib_bookmarked", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
|
||||||
|
|
||||||
|
fun filterCompletedAnime() = preferenceStore.getInt("pref_filter_animelib_completed", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
|
||||||
|
|
||||||
|
fun filterTrackingAnime(name: Int) = preferenceStore.getInt("pref_filter_animelib_tracked_$name", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Badges
|
// region Badges
|
||||||
|
|
|
@ -42,5 +42,5 @@ class SourcePreferences(
|
||||||
|
|
||||||
fun searchPinnedSourcesOnly() = preferenceStore.getBoolean("search_pinned_sources_only", false)
|
fun searchPinnedSourcesOnly() = preferenceStore.getBoolean("search_pinned_sources_only", false)
|
||||||
|
|
||||||
fun searchAnimePinnedSourcesOnly() = preferenceStore.getBoolean("search_pinned_anime_sources_only", false)
|
fun searchPinnedAnimeSourcesOnly() = preferenceStore.getBoolean("search_pinned_anime_sources_only", false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import androidx.work.NetworkType
|
||||||
import androidx.work.OneTimeWorkRequestBuilder
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import eu.kanade.domain.anime.interactor.GetAnime
|
|
||||||
import eu.kanade.domain.animetrack.interactor.GetAnimeTracks
|
import eu.kanade.domain.animetrack.interactor.GetAnimeTracks
|
||||||
import eu.kanade.domain.animetrack.interactor.InsertAnimeTrack
|
import eu.kanade.domain.animetrack.interactor.InsertAnimeTrack
|
||||||
import eu.kanade.domain.animetrack.model.toDbTrack
|
import eu.kanade.domain.animetrack.model.toDbTrack
|
||||||
|
|
|
@ -71,7 +71,7 @@ import eu.kanade.tachiyomi.animesource.getNameForAnimeInfo
|
||||||
import eu.kanade.tachiyomi.data.animedownload.model.AnimeDownload
|
import eu.kanade.tachiyomi.data.animedownload.model.AnimeDownload
|
||||||
import eu.kanade.tachiyomi.ui.anime.AnimeScreenState
|
import eu.kanade.tachiyomi.ui.anime.AnimeScreenState
|
||||||
import eu.kanade.tachiyomi.ui.anime.EpisodeItem
|
import eu.kanade.tachiyomi.ui.anime.EpisodeItem
|
||||||
import eu.kanade.tachiyomi.ui.player.setting.PlayerPreferences
|
import eu.kanade.tachiyomi.ui.player.settings.PlayerPreferences
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
|
@ -252,6 +252,7 @@ private fun AnimeScreenSmallImpl(
|
||||||
onClickDownload = onDownloadActionClicked,
|
onClickDownload = onDownloadActionClicked,
|
||||||
onClickEditCategory = onEditCategoryClicked,
|
onClickEditCategory = onEditCategoryClicked,
|
||||||
onClickMigrate = onMigrateClicked,
|
onClickMigrate = onMigrateClicked,
|
||||||
|
changeAnimeSkipIntro = changeAnimeSkipIntro,
|
||||||
actionModeCounter = episodes.count { it.selected },
|
actionModeCounter = episodes.count { it.selected },
|
||||||
onSelectAll = { onAllEpisodeSelected(true) },
|
onSelectAll = { onAllEpisodeSelected(true) },
|
||||||
onInvertSelection = { onInvertSelection() },
|
onInvertSelection = { onInvertSelection() },
|
||||||
|
|
|
@ -2,7 +2,6 @@ package eu.kanade.presentation.anime
|
||||||
|
|
||||||
import androidx.compose.animation.animateContentSize
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
@ -10,27 +9,19 @@ import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.IntrinsicSize
|
import androidx.compose.foundation.layout.IntrinsicSize
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.systemBars
|
import androidx.compose.foundation.layout.systemBars
|
||||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
import androidx.compose.foundation.layout.wrapContentSize
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.MoreVert
|
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
|
@ -39,11 +30,9 @@ import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.kanade.presentation.components.Divider
|
import eu.kanade.presentation.components.Divider
|
||||||
import eu.kanade.presentation.components.DropdownMenu
|
|
||||||
import eu.kanade.presentation.components.TrackLogoIcon
|
import eu.kanade.presentation.components.TrackLogoIcon
|
||||||
import eu.kanade.presentation.components.VerticalDivider
|
import eu.kanade.presentation.components.VerticalDivider
|
||||||
import eu.kanade.presentation.manga.TrackDetailsItem
|
import eu.kanade.presentation.manga.TrackDetailsItem
|
||||||
|
|
|
@ -5,10 +5,7 @@ import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.slideInVertically
|
import androidx.compose.animation.slideInVertically
|
||||||
import androidx.compose.animation.slideOutVertically
|
import androidx.compose.animation.slideOutVertically
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.border
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
|
|
@ -13,13 +13,13 @@ import eu.kanade.presentation.components.FastScrollLazyColumn
|
||||||
import eu.kanade.presentation.components.Scaffold
|
import eu.kanade.presentation.components.Scaffold
|
||||||
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.browse.animeextension.AnimeExtensionFilterState
|
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionFilterState
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AnimeExtensionFilterScreen(
|
fun AnimeExtensionFilterScreen(
|
||||||
navigateUp: () -> Unit,
|
navigateUp: () -> Unit,
|
||||||
state: AnimeExtensionFilterState.Success,
|
state: ExtensionFilterState.Success,
|
||||||
onClickToggle: (String) -> Unit,
|
onClickToggle: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
|
@ -31,13 +31,13 @@ fun AnimeExtensionFilterScreen(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) { contentPadding ->
|
) { contentPadding ->
|
||||||
if (state.isEmpty) {
|
if (state.isEmpty) {
|
||||||
EmptyScreen(
|
EmptyScreen(
|
||||||
textResource = R.string.empty_screen,
|
textResource = R.string.empty_screen,
|
||||||
modifier = Modifier.padding(contentPadding),
|
modifier = Modifier.padding(contentPadding),
|
||||||
)
|
)
|
||||||
return@Scaffold
|
return@Scaffold
|
||||||
}
|
}
|
||||||
AnimeExtensionFilterContent(
|
AnimeExtensionFilterContent(
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
state = state,
|
state = state,
|
||||||
|
@ -49,7 +49,7 @@ fun AnimeExtensionFilterScreen(
|
||||||
@Composable
|
@Composable
|
||||||
private fun AnimeExtensionFilterContent(
|
private fun AnimeExtensionFilterContent(
|
||||||
contentPadding: PaddingValues,
|
contentPadding: PaddingValues,
|
||||||
state: AnimeExtensionFilterState.Success,
|
state: ExtensionFilterState.Success,
|
||||||
onClickLang: (String) -> Unit,
|
onClickLang: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
|
@ -83,7 +83,7 @@ private fun AnimeSourcesFilterContent(
|
||||||
AnimeSourcesFilterItem(
|
AnimeSourcesFilterItem(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItemPlacement(),
|
||||||
source = source,
|
source = source,
|
||||||
enabled = "${source.id}" !in state.disabledSources,
|
isEnabled = "${source.id}" !in state.disabledSources,
|
||||||
onClickItem = onClickSource,
|
onClickItem = onClickSource,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,6 @@ import eu.kanade.tachiyomi.animesource.LocalAnimeSource
|
||||||
import eu.kanade.tachiyomi.ui.browse.animesource.AnimeSourcesState
|
import eu.kanade.tachiyomi.ui.browse.animesource.AnimeSourcesState
|
||||||
import eu.kanade.tachiyomi.ui.browse.animesource.browse.BrowseAnimeSourceScreenModel.Listing
|
import eu.kanade.tachiyomi.ui.browse.animesource.browse.BrowseAnimeSourceScreenModel.Listing
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AnimeSourcesScreen(
|
fun AnimeSourcesScreen(
|
||||||
|
|
|
@ -19,8 +19,8 @@ import eu.kanade.presentation.components.Scaffold
|
||||||
import eu.kanade.presentation.util.padding
|
import eu.kanade.presentation.util.padding
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
|
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
|
||||||
import eu.kanade.tachiyomi.ui.browse.animesource.globalsearch.GlobalAnimeSearchState
|
|
||||||
import eu.kanade.tachiyomi.ui.browse.animesource.globalsearch.AnimeSearchItemResult
|
import eu.kanade.tachiyomi.ui.browse.animesource.globalsearch.AnimeSearchItemResult
|
||||||
|
import eu.kanade.tachiyomi.ui.browse.animesource.globalsearch.GlobalAnimeSearchState
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
@ -10,7 +10,6 @@ import eu.kanade.presentation.anime.components.BaseAnimeListItem
|
||||||
import eu.kanade.presentation.components.AppBar
|
import eu.kanade.presentation.components.AppBar
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
import eu.kanade.presentation.components.FastScrollLazyColumn
|
import eu.kanade.presentation.components.FastScrollLazyColumn
|
||||||
import eu.kanade.presentation.components.LoadingScreen
|
|
||||||
import eu.kanade.presentation.components.Scaffold
|
import eu.kanade.presentation.components.Scaffold
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.browse.migration.anime.MigrateAnimeState
|
import eu.kanade.tachiyomi.ui.browse.migration.anime.MigrateAnimeState
|
||||||
|
|
|
@ -13,8 +13,8 @@ import eu.kanade.presentation.browse.components.GlobalSearchToolbar
|
||||||
import eu.kanade.presentation.components.LazyColumn
|
import eu.kanade.presentation.components.LazyColumn
|
||||||
import eu.kanade.presentation.components.Scaffold
|
import eu.kanade.presentation.components.Scaffold
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
|
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
|
||||||
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateAnimeSearchState
|
|
||||||
import eu.kanade.tachiyomi.ui.browse.animesource.globalsearch.AnimeSearchItemResult
|
import eu.kanade.tachiyomi.ui.browse.animesource.globalsearch.AnimeSearchItemResult
|
||||||
|
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateAnimeSearchState
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
@ -39,12 +39,12 @@ import eu.kanade.presentation.util.plus
|
||||||
import eu.kanade.presentation.util.secondaryItemAlpha
|
import eu.kanade.presentation.util.secondaryItemAlpha
|
||||||
import eu.kanade.presentation.util.topSmallPaddingValues
|
import eu.kanade.presentation.util.topSmallPaddingValues
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.browse.migration.animesources.MigrateAnimeSourcesState
|
import eu.kanade.tachiyomi.ui.browse.migration.animesources.MigrateAnimeSourceState
|
||||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MigrateAnimeSourceScreen(
|
fun MigrateAnimeSourceScreen(
|
||||||
state: MigrateAnimeSourcesState,
|
state: MigrateAnimeSourceState,
|
||||||
contentPadding: PaddingValues,
|
contentPadding: PaddingValues,
|
||||||
onClickItem: (AnimeSource) -> Unit,
|
onClickItem: (AnimeSource) -> Unit,
|
||||||
onToggleSortingDirection: () -> Unit,
|
onToggleSortingDirection: () -> Unit,
|
||||||
|
|
|
@ -13,8 +13,8 @@ import androidx.paging.LoadState
|
||||||
import androidx.paging.compose.LazyPagingItems
|
import androidx.paging.compose.LazyPagingItems
|
||||||
import eu.kanade.domain.anime.model.Anime
|
import eu.kanade.domain.anime.model.Anime
|
||||||
import eu.kanade.domain.anime.model.AnimeCover
|
import eu.kanade.domain.anime.model.AnimeCover
|
||||||
import eu.kanade.presentation.browse.components.BrowseSourceLoadingItem
|
|
||||||
import eu.kanade.presentation.browse.InLibraryBadge
|
import eu.kanade.presentation.browse.InLibraryBadge
|
||||||
|
import eu.kanade.presentation.browse.components.BrowseSourceLoadingItem
|
||||||
import eu.kanade.presentation.components.CommonMangaItemDefaults
|
import eu.kanade.presentation.components.CommonMangaItemDefaults
|
||||||
import eu.kanade.presentation.components.MangaComfortableGridItem
|
import eu.kanade.presentation.components.MangaComfortableGridItem
|
||||||
import eu.kanade.presentation.util.plus
|
import eu.kanade.presentation.util.plus
|
||||||
|
|
|
@ -13,8 +13,8 @@ import androidx.paging.LoadState
|
||||||
import androidx.paging.compose.LazyPagingItems
|
import androidx.paging.compose.LazyPagingItems
|
||||||
import eu.kanade.domain.anime.model.Anime
|
import eu.kanade.domain.anime.model.Anime
|
||||||
import eu.kanade.domain.anime.model.AnimeCover
|
import eu.kanade.domain.anime.model.AnimeCover
|
||||||
import eu.kanade.presentation.browse.components.BrowseSourceLoadingItem
|
|
||||||
import eu.kanade.presentation.browse.InLibraryBadge
|
import eu.kanade.presentation.browse.InLibraryBadge
|
||||||
|
import eu.kanade.presentation.browse.components.BrowseSourceLoadingItem
|
||||||
import eu.kanade.presentation.components.CommonMangaItemDefaults
|
import eu.kanade.presentation.components.CommonMangaItemDefaults
|
||||||
import eu.kanade.presentation.components.MangaCompactGridItem
|
import eu.kanade.presentation.components.MangaCompactGridItem
|
||||||
import eu.kanade.presentation.util.plus
|
import eu.kanade.presentation.util.plus
|
||||||
|
|
|
@ -10,8 +10,8 @@ import androidx.paging.compose.LazyPagingItems
|
||||||
import androidx.paging.compose.items
|
import androidx.paging.compose.items
|
||||||
import eu.kanade.domain.anime.model.Anime
|
import eu.kanade.domain.anime.model.Anime
|
||||||
import eu.kanade.domain.anime.model.AnimeCover
|
import eu.kanade.domain.anime.model.AnimeCover
|
||||||
import eu.kanade.presentation.browse.components.BrowseSourceLoadingItem
|
|
||||||
import eu.kanade.presentation.browse.InLibraryBadge
|
import eu.kanade.presentation.browse.InLibraryBadge
|
||||||
|
import eu.kanade.presentation.browse.components.BrowseSourceLoadingItem
|
||||||
import eu.kanade.presentation.components.CommonMangaItemDefaults
|
import eu.kanade.presentation.components.CommonMangaItemDefaults
|
||||||
import eu.kanade.presentation.components.LazyColumn
|
import eu.kanade.presentation.components.LazyColumn
|
||||||
import eu.kanade.presentation.components.MangaListItem
|
import eu.kanade.presentation.components.MangaListItem
|
||||||
|
|
|
@ -2,55 +2,32 @@ package eu.kanade.presentation.animehistory
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.outlined.DeleteSweep
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import eu.kanade.domain.animehistory.model.AnimeHistoryWithRelations
|
import eu.kanade.domain.animehistory.model.AnimeHistoryWithRelations
|
||||||
import eu.kanade.presentation.animehistory.components.AnimeHistoryContent
|
import eu.kanade.presentation.animehistory.components.AnimeHistoryContent
|
||||||
import eu.kanade.presentation.components.AppBarTitle
|
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
import eu.kanade.presentation.components.LoadingScreen
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
import eu.kanade.presentation.components.Scaffold
|
import eu.kanade.presentation.components.Scaffold
|
||||||
import eu.kanade.presentation.components.SearchToolbar
|
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.animehistory.AnimeHistoryScreenModel
|
import eu.kanade.tachiyomi.ui.history.anime.AnimeHistoryScreenModel
|
||||||
import eu.kanade.tachiyomi.ui.animehistory.AnimeHistoryState
|
import eu.kanade.tachiyomi.ui.history.anime.AnimeHistoryState
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AnimeHistoryScreen(
|
fun AnimeHistoryScreen(
|
||||||
state: AnimeHistoryState,
|
state: AnimeHistoryState,
|
||||||
|
contentPadding: PaddingValues,
|
||||||
snackbarHostState: SnackbarHostState,
|
snackbarHostState: SnackbarHostState,
|
||||||
onSearchQueryChange: (String?) -> Unit,
|
|
||||||
onClickCover: (animeId: Long) -> Unit,
|
onClickCover: (animeId: Long) -> Unit,
|
||||||
onClickResume: (animeId: Long, episodeId: Long) -> Unit,
|
onClickResume: (animeId: Long, episodeId: Long) -> Unit,
|
||||||
onDialogChange: (AnimeHistoryScreenModel.Dialog?) -> Unit,
|
onDialogChange: (AnimeHistoryScreenModel.Dialog?) -> Unit,
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = { scrollBehavior ->
|
|
||||||
SearchToolbar(
|
|
||||||
titleContent = { AppBarTitle(stringResource(R.string.history)) },
|
|
||||||
searchQuery = state.searchQuery,
|
|
||||||
onChangeSearchQuery = onSearchQueryChange,
|
|
||||||
actions = {
|
|
||||||
IconButton(onClick = { onDialogChange(AnimeHistoryScreenModel.Dialog.DeleteAll) }) {
|
|
||||||
Icon(
|
|
||||||
Icons.Outlined.DeleteSweep,
|
|
||||||
contentDescription = stringResource(R.string.pref_clear_history),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scrollBehavior = scrollBehavior,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||||
) { contentPadding ->
|
) { _ ->
|
||||||
state.list.let {
|
state.list.let {
|
||||||
if (it == null) {
|
if (it == null) {
|
||||||
LoadingScreen(modifier = Modifier.padding(contentPadding))
|
LoadingScreen(modifier = Modifier.padding(contentPadding))
|
||||||
|
|
|
@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.calculateStartPadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.State
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
|
|
@ -2,6 +2,7 @@ package eu.kanade.presentation.animelib.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
package eu.kanade.presentation.animelib.components
|
package eu.kanade.presentation.animelib.components
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
@ -15,16 +10,13 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import eu.kanade.core.prefs.PreferenceMutableState
|
import eu.kanade.core.prefs.PreferenceMutableState
|
||||||
import eu.kanade.domain.animelib.model.AnimelibAnime
|
import eu.kanade.domain.animelib.model.AnimelibAnime
|
||||||
import eu.kanade.domain.library.model.LibraryDisplayMode
|
import eu.kanade.domain.library.model.LibraryDisplayMode
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
|
||||||
import eu.kanade.presentation.components.HorizontalPager
|
import eu.kanade.presentation.components.HorizontalPager
|
||||||
import eu.kanade.presentation.components.PagerState
|
import eu.kanade.presentation.components.PagerState
|
||||||
import eu.kanade.presentation.library.components.LibraryPagerEmptyScreen
|
import eu.kanade.presentation.library.components.LibraryPagerEmptyScreen
|
||||||
import eu.kanade.presentation.util.plus
|
import eu.kanade.presentation.util.plus
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.ui.animelib.AnimelibItem
|
import eu.kanade.tachiyomi.ui.animelib.AnimelibItem
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
@ -1,18 +1,11 @@
|
||||||
package eu.kanade.presentation.animeupdates
|
package eu.kanade.presentation.animeupdates
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.outlined.FlipToBack
|
|
||||||
import androidx.compose.material.icons.outlined.Refresh
|
|
||||||
import androidx.compose.material.icons.outlined.SelectAll
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
@ -30,12 +23,11 @@ import eu.kanade.presentation.components.FastScrollLazyColumn
|
||||||
import eu.kanade.presentation.components.LoadingScreen
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
import eu.kanade.presentation.components.PullRefresh
|
import eu.kanade.presentation.components.PullRefresh
|
||||||
import eu.kanade.presentation.components.Scaffold
|
import eu.kanade.presentation.components.Scaffold
|
||||||
import eu.kanade.presentation.updates.updatesLastUpdatedItem
|
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.animedownload.model.AnimeDownload
|
import eu.kanade.tachiyomi.data.animedownload.model.AnimeDownload
|
||||||
import eu.kanade.tachiyomi.ui.animeupdates.AnimeUpdatesItem
|
import eu.kanade.tachiyomi.ui.player.settings.PlayerPreferences
|
||||||
import eu.kanade.tachiyomi.ui.animeupdates.AnimeUpdatesState
|
import eu.kanade.tachiyomi.ui.updates.anime.AnimeUpdatesItem
|
||||||
import eu.kanade.tachiyomi.ui.player.setting.PlayerPreferences
|
import eu.kanade.tachiyomi.ui.updates.anime.AnimeUpdatesState
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
|
@ -46,6 +38,7 @@ import kotlin.time.Duration.Companion.seconds
|
||||||
fun AnimeUpdateScreen(
|
fun AnimeUpdateScreen(
|
||||||
state: AnimeUpdatesState,
|
state: AnimeUpdatesState,
|
||||||
snackbarHostState: SnackbarHostState,
|
snackbarHostState: SnackbarHostState,
|
||||||
|
contentPadding: PaddingValues,
|
||||||
lastUpdated: Long,
|
lastUpdated: Long,
|
||||||
relativeTime: Int,
|
relativeTime: Int,
|
||||||
onClickCover: (AnimeUpdatesItem) -> Unit,
|
onClickCover: (AnimeUpdatesItem) -> Unit,
|
||||||
|
@ -54,10 +47,10 @@ fun AnimeUpdateScreen(
|
||||||
onUpdateLibrary: () -> Boolean,
|
onUpdateLibrary: () -> Boolean,
|
||||||
onDownloadEpisode: (List<AnimeUpdatesItem>, EpisodeDownloadAction) -> Unit,
|
onDownloadEpisode: (List<AnimeUpdatesItem>, EpisodeDownloadAction) -> Unit,
|
||||||
onMultiBookmarkClicked: (List<AnimeUpdatesItem>, bookmark: Boolean) -> Unit,
|
onMultiBookmarkClicked: (List<AnimeUpdatesItem>, bookmark: Boolean) -> Unit,
|
||||||
onMultiMarkAsReadClicked: (List<AnimeUpdatesItem>, read: Boolean) -> Unit,
|
onMultiMarkAsSeenClicked: (List<AnimeUpdatesItem>, seen: Boolean) -> Unit,
|
||||||
onMultiDeleteClicked: (List<AnimeUpdatesItem>) -> Unit,
|
onMultiDeleteClicked: (List<AnimeUpdatesItem>) -> Unit,
|
||||||
onUpdateSelected: (AnimeUpdatesItem, Boolean, Boolean, Boolean) -> Unit,
|
onUpdateSelected: (AnimeUpdatesItem, Boolean, Boolean, Boolean) -> Unit,
|
||||||
onOpenEpisode: (List<AnimeUpdatesItem>, context: Context, altPlayer: Boolean) -> Unit,
|
onOpenEpisode: (AnimeUpdatesItem, altPlayer: Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
BackHandler(enabled = state.selectionMode, onBack = { onSelectAll(false) })
|
BackHandler(enabled = state.selectionMode, onBack = { onSelectAll(false) })
|
||||||
|
|
||||||
|
@ -69,13 +62,13 @@ fun AnimeUpdateScreen(
|
||||||
selected = state.selected,
|
selected = state.selected,
|
||||||
onDownloadEpisode = onDownloadEpisode,
|
onDownloadEpisode = onDownloadEpisode,
|
||||||
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
||||||
onMultiMarkAsSeenClicked = onMultiMarkAsReadClicked,
|
onMultiMarkAsSeenClicked = onMultiMarkAsSeenClicked,
|
||||||
onMultiDeleteClicked = onMultiDeleteClicked,
|
onMultiDeleteClicked = onMultiDeleteClicked,
|
||||||
onOpenEpisode = onOpenEpisode,
|
onOpenEpisode = onOpenEpisode,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||||
) { contentPadding ->
|
) {
|
||||||
when {
|
when {
|
||||||
state.isLoading -> LoadingScreen(modifier = Modifier.padding(contentPadding))
|
state.isLoading -> LoadingScreen(modifier = Modifier.padding(contentPadding))
|
||||||
state.items.isEmpty() -> EmptyScreen(
|
state.items.isEmpty() -> EmptyScreen(
|
||||||
|
@ -105,14 +98,14 @@ fun AnimeUpdateScreen(
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
) {
|
) {
|
||||||
if (lastUpdated > 0L) {
|
if (lastUpdated > 0L) {
|
||||||
updatesLastUpdatedItem(lastUpdated)
|
animeupdatesLastUpdatedItem(lastUpdated)
|
||||||
}
|
}
|
||||||
animeupdatesUiItems(
|
animeupdatesUiItems(
|
||||||
uiModels = state.getUiModel(context, relativeTime),
|
uiModels = state.getUiModel(context, relativeTime),
|
||||||
selectionMode = state.selectionMode,
|
selectionMode = state.selectionMode,
|
||||||
onUpdateSelected = onUpdateSelected,
|
onUpdateSelected = onUpdateSelected,
|
||||||
onClickCover = onClickCover,
|
onClickCover = onClickCover,
|
||||||
onClickUpdate = { onOpenEpisode },
|
onClickUpdate = onOpenEpisode,
|
||||||
onDownloadEpisode = onDownloadEpisode,
|
onDownloadEpisode = onDownloadEpisode,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -129,10 +122,9 @@ private fun AnimeUpdatesBottomBar(
|
||||||
onMultiBookmarkClicked: (List<AnimeUpdatesItem>, bookmark: Boolean) -> Unit,
|
onMultiBookmarkClicked: (List<AnimeUpdatesItem>, bookmark: Boolean) -> Unit,
|
||||||
onMultiMarkAsSeenClicked: (List<AnimeUpdatesItem>, seen: Boolean) -> Unit,
|
onMultiMarkAsSeenClicked: (List<AnimeUpdatesItem>, seen: Boolean) -> Unit,
|
||||||
onMultiDeleteClicked: (List<AnimeUpdatesItem>) -> Unit,
|
onMultiDeleteClicked: (List<AnimeUpdatesItem>) -> Unit,
|
||||||
onOpenEpisode: (List<AnimeUpdatesItem>, context: Context, altPlayer: Boolean) -> Unit,
|
onOpenEpisode: (AnimeUpdatesItem, altPlayer: Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
val playerPreferences: PlayerPreferences = Injekt.get()
|
val playerPreferences: PlayerPreferences = Injekt.get()
|
||||||
val context = LocalContext.current
|
|
||||||
AnimeBottomActionMenu(
|
AnimeBottomActionMenu(
|
||||||
visible = selected.isNotEmpty(),
|
visible = selected.isNotEmpty(),
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
@ -157,10 +149,10 @@ private fun AnimeUpdatesBottomBar(
|
||||||
onMultiDeleteClicked(selected)
|
onMultiDeleteClicked(selected)
|
||||||
}.takeIf { selected.fastAny { it.downloadStateProvider() == AnimeDownload.State.DOWNLOADED } },
|
}.takeIf { selected.fastAny { it.downloadStateProvider() == AnimeDownload.State.DOWNLOADED } },
|
||||||
onExternalClicked = {
|
onExternalClicked = {
|
||||||
onOpenEpisode(selected, context, true)
|
onOpenEpisode(selected[0], true)
|
||||||
}.takeIf { !playerPreferences.alwaysUseExternalPlayer().get() && selected.size == 1 },
|
}.takeIf { !playerPreferences.alwaysUseExternalPlayer().get() && selected.size == 1 },
|
||||||
onInternalClicked = {
|
onInternalClicked = {
|
||||||
onOpenEpisode(selected, context,false)
|
onOpenEpisode(selected[0], true)
|
||||||
}.takeIf { playerPreferences.alwaysUseExternalPlayer().get() && selected.size == 1 },
|
}.takeIf { playerPreferences.alwaysUseExternalPlayer().get() && selected.size == 1 },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ import eu.kanade.presentation.util.padding
|
||||||
import eu.kanade.presentation.util.selectedBackground
|
import eu.kanade.presentation.util.selectedBackground
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.animedownload.model.AnimeDownload
|
import eu.kanade.tachiyomi.data.animedownload.model.AnimeDownload
|
||||||
import eu.kanade.tachiyomi.ui.animeupdates.AnimeUpdatesItem
|
import eu.kanade.tachiyomi.ui.updates.anime.AnimeUpdatesItem
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import kotlin.time.Duration.Companion.minutes
|
import kotlin.time.Duration.Companion.minutes
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ fun LazyListScope.animeupdatesUiItems(
|
||||||
selectionMode: Boolean,
|
selectionMode: Boolean,
|
||||||
onUpdateSelected: (AnimeUpdatesItem, Boolean, Boolean, Boolean) -> Unit,
|
onUpdateSelected: (AnimeUpdatesItem, Boolean, Boolean, Boolean) -> Unit,
|
||||||
onClickCover: (AnimeUpdatesItem) -> Unit,
|
onClickCover: (AnimeUpdatesItem) -> Unit,
|
||||||
onClickUpdate: (AnimeUpdatesItem) -> Unit,
|
onClickUpdate: (AnimeUpdatesItem, altPlayer: Boolean) -> Unit,
|
||||||
onDownloadEpisode: (List<AnimeUpdatesItem>, EpisodeDownloadAction) -> Unit,
|
onDownloadEpisode: (List<AnimeUpdatesItem>, EpisodeDownloadAction) -> Unit,
|
||||||
) {
|
) {
|
||||||
items(
|
items(
|
||||||
|
@ -119,7 +119,7 @@ fun LazyListScope.animeupdatesUiItems(
|
||||||
onClick = {
|
onClick = {
|
||||||
when {
|
when {
|
||||||
selectionMode -> onUpdateSelected(updatesItem, !updatesItem.selected, true, false)
|
selectionMode -> onUpdateSelected(updatesItem, !updatesItem.selected, true, false)
|
||||||
else -> onClickUpdate(updatesItem)
|
else -> onClickUpdate(updatesItem, false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onClickCover = { onClickCover(updatesItem) }.takeIf { !selectionMode },
|
onClickCover = { onClickCover(updatesItem) }.takeIf { !selectionMode },
|
||||||
|
|
|
@ -6,49 +6,40 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
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.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import eu.kanade.domain.category.model.Category
|
import eu.kanade.domain.category.model.Category
|
||||||
import eu.kanade.presentation.category.components.CategoryContent
|
import eu.kanade.presentation.category.components.CategoryContent
|
||||||
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
|
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
|
||||||
import eu.kanade.presentation.components.AppBar
|
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
import eu.kanade.presentation.components.Scaffold
|
import eu.kanade.presentation.components.Scaffold
|
||||||
import eu.kanade.presentation.util.padding
|
import eu.kanade.presentation.util.padding
|
||||||
import eu.kanade.presentation.util.plus
|
import eu.kanade.presentation.util.plus
|
||||||
import eu.kanade.presentation.util.topSmallPaddingValues
|
import eu.kanade.presentation.util.topSmallPaddingValues
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.animecategory.AnimeCategoryScreenState
|
import eu.kanade.tachiyomi.ui.category.anime.AnimeCategoryScreenState
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AnimeCategoryScreen(
|
fun AnimeCategoryScreen(
|
||||||
state: AnimeCategoryScreenState.Success,
|
state: AnimeCategoryScreenState.Success,
|
||||||
|
contentPadding: PaddingValues,
|
||||||
onClickCreate: () -> Unit,
|
onClickCreate: () -> Unit,
|
||||||
onClickRename: (Category) -> Unit,
|
onClickRename: (Category) -> Unit,
|
||||||
onClickDelete: (Category) -> Unit,
|
onClickDelete: (Category) -> Unit,
|
||||||
onClickMoveUp: (Category) -> Unit,
|
onClickMoveUp: (Category) -> Unit,
|
||||||
onClickMoveDown: (Category) -> Unit,
|
onClickMoveDown: (Category) -> Unit,
|
||||||
navigateUp: () -> Unit,
|
|
||||||
) {
|
) {
|
||||||
val lazyListState = rememberLazyListState()
|
val lazyListState = rememberLazyListState()
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = { scrollBehavior ->
|
|
||||||
AppBar(
|
|
||||||
title = stringResource(R.string.action_edit_categories),
|
|
||||||
navigateUp = navigateUp,
|
|
||||||
scrollBehavior = scrollBehavior,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
CategoryFloatingActionButton(
|
CategoryFloatingActionButton(
|
||||||
lazyListState = lazyListState,
|
lazyListState = lazyListState,
|
||||||
onCreate = onClickCreate,
|
onCreate = onClickCreate,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) { paddingValues ->
|
) {
|
||||||
if (state.isEmpty) {
|
if (state.isEmpty) {
|
||||||
EmptyScreen(
|
EmptyScreen(
|
||||||
textResource = R.string.information_empty_category,
|
textResource = R.string.information_empty_category,
|
||||||
modifier = Modifier.padding(paddingValues),
|
modifier = Modifier.padding(contentPadding),
|
||||||
)
|
)
|
||||||
return@Scaffold
|
return@Scaffold
|
||||||
}
|
}
|
||||||
|
@ -56,7 +47,7 @@ fun AnimeCategoryScreen(
|
||||||
CategoryContent(
|
CategoryContent(
|
||||||
categories = state.categories,
|
categories = state.categories,
|
||||||
lazyListState = lazyListState,
|
lazyListState = lazyListState,
|
||||||
paddingValues = paddingValues + topSmallPaddingValues + PaddingValues(horizontal = MaterialTheme.padding.medium),
|
paddingValues = contentPadding + topSmallPaddingValues + PaddingValues(horizontal = MaterialTheme.padding.medium),
|
||||||
onClickRename = onClickRename,
|
onClickRename = onClickRename,
|
||||||
onClickDelete = onClickDelete,
|
onClickDelete = onClickDelete,
|
||||||
onMoveUp = onClickMoveUp,
|
onMoveUp = onClickMoveUp,
|
||||||
|
|
|
@ -6,49 +6,40 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
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.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import eu.kanade.domain.category.model.Category
|
import eu.kanade.domain.category.model.Category
|
||||||
import eu.kanade.presentation.category.components.CategoryContent
|
import eu.kanade.presentation.category.components.CategoryContent
|
||||||
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
|
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
|
||||||
import eu.kanade.presentation.components.AppBar
|
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
import eu.kanade.presentation.components.Scaffold
|
import eu.kanade.presentation.components.Scaffold
|
||||||
import eu.kanade.presentation.util.padding
|
import eu.kanade.presentation.util.padding
|
||||||
import eu.kanade.presentation.util.plus
|
import eu.kanade.presentation.util.plus
|
||||||
import eu.kanade.presentation.util.topSmallPaddingValues
|
import eu.kanade.presentation.util.topSmallPaddingValues
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.category.CategoryScreenState
|
import eu.kanade.tachiyomi.ui.category.manga.CategoryScreenState
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CategoryScreen(
|
fun CategoryScreen(
|
||||||
state: CategoryScreenState.Success,
|
state: CategoryScreenState.Success,
|
||||||
|
contentPadding: PaddingValues,
|
||||||
onClickCreate: () -> Unit,
|
onClickCreate: () -> Unit,
|
||||||
onClickRename: (Category) -> Unit,
|
onClickRename: (Category) -> Unit,
|
||||||
onClickDelete: (Category) -> Unit,
|
onClickDelete: (Category) -> Unit,
|
||||||
onClickMoveUp: (Category) -> Unit,
|
onClickMoveUp: (Category) -> Unit,
|
||||||
onClickMoveDown: (Category) -> Unit,
|
onClickMoveDown: (Category) -> Unit,
|
||||||
navigateUp: () -> Unit,
|
|
||||||
) {
|
) {
|
||||||
val lazyListState = rememberLazyListState()
|
val lazyListState = rememberLazyListState()
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = { scrollBehavior ->
|
|
||||||
AppBar(
|
|
||||||
title = stringResource(R.string.action_edit_categories),
|
|
||||||
navigateUp = navigateUp,
|
|
||||||
scrollBehavior = scrollBehavior,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
CategoryFloatingActionButton(
|
CategoryFloatingActionButton(
|
||||||
lazyListState = lazyListState,
|
lazyListState = lazyListState,
|
||||||
onCreate = onClickCreate,
|
onCreate = onClickCreate,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) { paddingValues ->
|
) {
|
||||||
if (state.isEmpty) {
|
if (state.isEmpty) {
|
||||||
EmptyScreen(
|
EmptyScreen(
|
||||||
textResource = R.string.information_empty_category,
|
textResource = R.string.information_empty_category,
|
||||||
modifier = Modifier.padding(paddingValues),
|
modifier = Modifier.padding(contentPadding),
|
||||||
)
|
)
|
||||||
return@Scaffold
|
return@Scaffold
|
||||||
}
|
}
|
||||||
|
@ -56,7 +47,7 @@ fun CategoryScreen(
|
||||||
CategoryContent(
|
CategoryContent(
|
||||||
categories = state.categories,
|
categories = state.categories,
|
||||||
lazyListState = lazyListState,
|
lazyListState = lazyListState,
|
||||||
paddingValues = paddingValues + topSmallPaddingValues + PaddingValues(horizontal = MaterialTheme.padding.medium),
|
paddingValues = contentPadding + topSmallPaddingValues + PaddingValues(horizontal = MaterialTheme.padding.medium),
|
||||||
onClickRename = onClickRename,
|
onClickRename = onClickRename,
|
||||||
onClickDelete = onClickDelete,
|
onClickDelete = onClickDelete,
|
||||||
onMoveUp = onClickMoveUp,
|
onMoveUp = onClickMoveUp,
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package eu.kanade.presentation.components
|
package eu.kanade.presentation.components
|
||||||
|
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
@ -30,6 +33,7 @@ import androidx.compose.runtime.key
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
@ -149,22 +153,41 @@ fun AppBar(
|
||||||
fun AppBarTitle(
|
fun AppBarTitle(
|
||||||
title: String?,
|
title: String?,
|
||||||
subtitle: String? = null,
|
subtitle: String? = null,
|
||||||
|
count: Int = 0,
|
||||||
) {
|
) {
|
||||||
Column {
|
if (count > 0) {
|
||||||
title?.let {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Text(
|
Text(
|
||||||
text = it,
|
text = title!!,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
|
modifier = Modifier.weight(1f, false),
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
|
val pillAlpha = if (isSystemInDarkTheme()) 0.12f else 0.08f
|
||||||
|
Pill(
|
||||||
|
text = "$count",
|
||||||
|
modifier = Modifier.padding(start = 4.dp),
|
||||||
|
color = MaterialTheme.colorScheme.onBackground.copy(alpha = pillAlpha),
|
||||||
|
fontSize = 14.sp,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
subtitle?.let {
|
} else {
|
||||||
Text(
|
Column {
|
||||||
text = it,
|
title?.let {
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
Text(
|
||||||
maxLines = 1,
|
text = it,
|
||||||
overflow = TextOverflow.Ellipsis,
|
maxLines = 1,
|
||||||
)
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
subtitle?.let {
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -317,8 +340,6 @@ fun SearchToolbar(
|
||||||
key("actions") { actions() }
|
key("actions") { actions() }
|
||||||
},
|
},
|
||||||
isActionMode = false,
|
isActionMode = false,
|
||||||
downloadedOnlyMode = downloadedOnlyMode,
|
|
||||||
incognitoMode = incognitoMode,
|
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
onCancelActionMode = cancelAction,
|
onCancelActionMode = cancelAction,
|
||||||
)
|
)
|
||||||
|
|
|
@ -32,15 +32,12 @@ fun TabbedScreen(
|
||||||
startIndex: Int? = null,
|
startIndex: Int? = null,
|
||||||
searchQuery: String? = null,
|
searchQuery: String? = null,
|
||||||
onChangeSearchQuery: (String?) -> Unit = {},
|
onChangeSearchQuery: (String?) -> Unit = {},
|
||||||
incognitoMode: Boolean = false,
|
|
||||||
downloadedOnlyMode: Boolean = false,
|
|
||||||
state: PagerState = rememberPagerState(),
|
state: PagerState = rememberPagerState(),
|
||||||
scrollable: Boolean = false,
|
scrollable: Boolean = false,
|
||||||
searchQueryAnime: String? = null,
|
searchQueryAnime: String? = null,
|
||||||
onChangeSearchQueryAnime: (String?) -> Unit = {},
|
onChangeSearchQueryAnime: (String?) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val state = rememberPagerState()
|
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
|
||||||
LaunchedEffect(startIndex) {
|
LaunchedEffect(startIndex) {
|
||||||
|
@ -65,9 +62,8 @@ fun TabbedScreen(
|
||||||
else -> onChangeSearchQueryAnime
|
else -> onChangeSearchQueryAnime
|
||||||
}
|
}
|
||||||
|
|
||||||
val appBarTitleText = if (tab.numberTitle == 0) stringResource(titleRes) else tab.numberTitle.toString()
|
|
||||||
SearchToolbar(
|
SearchToolbar(
|
||||||
titleContent = { AppBarTitle(appBarTitleText) },
|
titleContent = { AppBarTitle(stringResource(titleRes), null, tab.numberTitle) },
|
||||||
searchEnabled = searchEnabled,
|
searchEnabled = searchEnabled,
|
||||||
searchQuery = if (searchEnabled) actualQuery else null,
|
searchQuery = if (searchEnabled) actualQuery else null,
|
||||||
onChangeSearchQuery = actualOnChange,
|
onChangeSearchQuery = actualOnChange,
|
||||||
|
|
|
@ -2,55 +2,32 @@ package eu.kanade.presentation.history
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.outlined.DeleteSweep
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import eu.kanade.domain.history.model.HistoryWithRelations
|
import eu.kanade.domain.history.model.HistoryWithRelations
|
||||||
import eu.kanade.presentation.components.AppBarTitle
|
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
import eu.kanade.presentation.components.LoadingScreen
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
import eu.kanade.presentation.components.Scaffold
|
import eu.kanade.presentation.components.Scaffold
|
||||||
import eu.kanade.presentation.components.SearchToolbar
|
|
||||||
import eu.kanade.presentation.history.components.HistoryContent
|
import eu.kanade.presentation.history.components.HistoryContent
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
|
import eu.kanade.tachiyomi.ui.history.manga.HistoryState
|
||||||
import eu.kanade.tachiyomi.ui.history.HistoryState
|
import eu.kanade.tachiyomi.ui.history.manga.MangaHistoryScreenModel
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HistoryScreen(
|
fun HistoryScreen(
|
||||||
state: HistoryState,
|
state: HistoryState,
|
||||||
|
contentPadding: PaddingValues,
|
||||||
snackbarHostState: SnackbarHostState,
|
snackbarHostState: SnackbarHostState,
|
||||||
onSearchQueryChange: (String?) -> Unit,
|
|
||||||
onClickCover: (mangaId: Long) -> Unit,
|
onClickCover: (mangaId: Long) -> Unit,
|
||||||
onClickResume: (mangaId: Long, chapterId: Long) -> Unit,
|
onClickResume: (mangaId: Long, chapterId: Long) -> Unit,
|
||||||
onDialogChange: (HistoryScreenModel.Dialog?) -> Unit,
|
onDialogChange: (MangaHistoryScreenModel.Dialog?) -> Unit,
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = { scrollBehavior ->
|
|
||||||
SearchToolbar(
|
|
||||||
titleContent = { AppBarTitle(stringResource(R.string.history)) },
|
|
||||||
searchQuery = state.searchQuery,
|
|
||||||
onChangeSearchQuery = onSearchQueryChange,
|
|
||||||
actions = {
|
|
||||||
IconButton(onClick = { onDialogChange(HistoryScreenModel.Dialog.DeleteAll) }) {
|
|
||||||
Icon(
|
|
||||||
Icons.Outlined.DeleteSweep,
|
|
||||||
contentDescription = stringResource(R.string.pref_clear_history),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scrollBehavior = scrollBehavior,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||||
) { contentPadding ->
|
) { _ ->
|
||||||
state.list.let {
|
state.list.let {
|
||||||
if (it == null) {
|
if (it == null) {
|
||||||
LoadingScreen(modifier = Modifier.padding(contentPadding))
|
LoadingScreen(modifier = Modifier.padding(contentPadding))
|
||||||
|
@ -70,7 +47,7 @@ fun HistoryScreen(
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
onClickCover = { history -> onClickCover(history.mangaId) },
|
onClickCover = { history -> onClickCover(history.mangaId) },
|
||||||
onClickResume = { history -> onClickResume(history.mangaId, history.chapterId) },
|
onClickResume = { history -> onClickResume(history.mangaId, history.chapterId) },
|
||||||
onClickDelete = { item -> onDialogChange(HistoryScreenModel.Dialog.Delete(item)) },
|
onClickDelete = { item -> onDialogChange(MangaHistoryScreenModel.Dialog.Delete(item)) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,7 +102,7 @@ fun AnimeHistoryItem(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.clickable(onClick = onClickResume)
|
.clickable(onClick = onClickResume)
|
||||||
.height(HISTORY_ITEM_HEIGHT)
|
.height(HISTORY_ITEM_HEIGHT)
|
||||||
.padding(horizontal = horizontalPadding, vertical = 8.dp),
|
.padding(horizontal = MaterialTheme.padding.medium, vertical = MaterialTheme.padding.small),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
MangaCover.Book(
|
MangaCover.Book(
|
||||||
|
@ -113,7 +113,7 @@ fun AnimeHistoryItem(
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.padding(start = horizontalPadding, end = 8.dp),
|
.padding(start = MaterialTheme.padding.medium, end = MaterialTheme.padding.small),
|
||||||
) {
|
) {
|
||||||
val textStyle = MaterialTheme.typography.bodyMedium
|
val textStyle = MaterialTheme.typography.bodyMedium
|
||||||
Text(
|
Text(
|
||||||
|
|
|
@ -98,7 +98,7 @@ fun TrackChapterSelector(
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
isAnime: Boolean,
|
isAnime: Boolean,
|
||||||
) {
|
) {
|
||||||
val titleText = when(isAnime) {
|
val titleText = when (isAnime) {
|
||||||
true -> R.string.chapters
|
true -> R.string.chapters
|
||||||
false -> R.string.episodes
|
false -> R.string.episodes
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import androidx.compose.ui.platform.LocalUriHandler
|
||||||
import androidx.compose.ui.res.pluralStringResource
|
import androidx.compose.ui.res.pluralStringResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.res.vectorResource
|
import androidx.compose.ui.res.vectorResource
|
||||||
|
import eu.kanade.domain.library.service.LibraryPreferences
|
||||||
import eu.kanade.presentation.components.Divider
|
import eu.kanade.presentation.components.Divider
|
||||||
import eu.kanade.presentation.components.Scaffold
|
import eu.kanade.presentation.components.Scaffold
|
||||||
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||||
|
@ -33,9 +34,9 @@ import eu.kanade.presentation.components.WarningBanner
|
||||||
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
|
||||||
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.more.AnimeDownloadQueueState
|
|
||||||
import eu.kanade.tachiyomi.ui.more.DownloadQueueState
|
import eu.kanade.tachiyomi.ui.more.DownloadQueueState
|
||||||
import eu.kanade.tachiyomi.util.Constants
|
import eu.kanade.tachiyomi.util.Constants
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MoreScreen(
|
fun MoreScreen(
|
||||||
|
@ -45,9 +46,8 @@ fun MoreScreen(
|
||||||
incognitoMode: Boolean,
|
incognitoMode: Boolean,
|
||||||
onIncognitoModeChange: (Boolean) -> Unit,
|
onIncognitoModeChange: (Boolean) -> Unit,
|
||||||
isFDroid: Boolean,
|
isFDroid: Boolean,
|
||||||
onClickHistory: () -> Unit,
|
onClickAlt: () -> Unit,
|
||||||
onClickDownloadQueue: () -> Unit,
|
onClickDownloadQueue: () -> Unit,
|
||||||
onClickAnimeCategories: () -> Unit,
|
|
||||||
onClickCategories: () -> Unit,
|
onClickCategories: () -> Unit,
|
||||||
onClickStats: () -> Unit,
|
onClickStats: () -> Unit,
|
||||||
onClickBackupAndRestore: () -> Unit,
|
onClickBackupAndRestore: () -> Unit,
|
||||||
|
@ -101,22 +101,24 @@ fun MoreScreen(
|
||||||
|
|
||||||
item { Divider() }
|
item { Divider() }
|
||||||
|
|
||||||
|
val libraryPreferences: LibraryPreferences by injectLazy()
|
||||||
|
|
||||||
item {
|
item {
|
||||||
val bottomNavStyle = libraryPreferences.bottomNavStyle().get()
|
val bottomNavStyle = libraryPreferences.bottomNavStyle().get()
|
||||||
val titleRes = when (bottomNavStyle) {
|
val titleRes = when (bottomNavStyle) {
|
||||||
|
0 -> R.string.label_recent_manga
|
||||||
1 -> R.string.label_recent_updates
|
1 -> R.string.label_recent_updates
|
||||||
2 -> R.string.label_manga
|
else -> R.string.label_manga
|
||||||
else -> R.string.label_recent_manga
|
|
||||||
}
|
}
|
||||||
val icon = when (bottomNavStyle) {
|
val icon = when (bottomNavStyle) {
|
||||||
|
0 -> Icons.Outlined.History
|
||||||
1 -> ImageVector.vectorResource(id = R.drawable.ic_updates_outline_24dp)
|
1 -> ImageVector.vectorResource(id = R.drawable.ic_updates_outline_24dp)
|
||||||
2 -> Icons.Outlined.CollectionsBookmark
|
else -> Icons.Outlined.CollectionsBookmark
|
||||||
else -> Icons.Outlined.History
|
|
||||||
}
|
}
|
||||||
TextPreferenceWidget(
|
TextPreferenceWidget(
|
||||||
title = stringResource(titleRes),
|
title = stringResource(titleRes),
|
||||||
icon = icon,
|
icon = icon,
|
||||||
onPreferenceClick = onClickHistory,
|
onPreferenceClick = onClickAlt,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +153,7 @@ fun MoreScreen(
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
TextPreferenceWidget(
|
TextPreferenceWidget(
|
||||||
title = stringResource(R.string.categories),
|
title = stringResource(R.string.general_categories),
|
||||||
icon = Icons.Outlined.Label,
|
icon = Icons.Outlined.Label,
|
||||||
onPreferenceClick = onClickCategories,
|
onPreferenceClick = onClickCategories,
|
||||||
)
|
)
|
||||||
|
|
|
@ -199,8 +199,10 @@ object SettingsAdvancedScreen : SearchableSettings {
|
||||||
Preference.PreferenceItem.TextPreference(
|
Preference.PreferenceItem.TextPreference(
|
||||||
title = stringResource(R.string.pref_invalidate_download_cache),
|
title = stringResource(R.string.pref_invalidate_download_cache),
|
||||||
subtitle = stringResource(R.string.pref_invalidate_download_cache_summary),
|
subtitle = stringResource(R.string.pref_invalidate_download_cache_summary),
|
||||||
onClick = { Injekt.get<DownloadCache>().invalidateCache()
|
onClick = {
|
||||||
Injekt.get<AnimeDownloadCache>().invalidateCache() },
|
Injekt.get<DownloadCache>().invalidateCache()
|
||||||
|
Injekt.get<AnimeDownloadCache>().invalidateCache()
|
||||||
|
},
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.TextPreference(
|
Preference.PreferenceItem.TextPreference(
|
||||||
title = stringResource(R.string.pref_clear_database),
|
title = stringResource(R.string.pref_clear_database),
|
||||||
|
|
|
@ -51,6 +51,15 @@ object SettingsBrowseScreen : SearchableSettings {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Preference.PreferenceGroup(
|
||||||
|
title = stringResource(R.string.action_global_search),
|
||||||
|
preferenceItems = listOf(
|
||||||
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
|
pref = sourcePreferences.searchPinnedAnimeSourcesOnly(),
|
||||||
|
title = stringResource(R.string.pref_search_pinned_sources_only),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
Preference.PreferenceGroup(
|
Preference.PreferenceGroup(
|
||||||
title = stringResource(R.string.pref_category_nsfw_content),
|
title = stringResource(R.string.pref_category_nsfw_content),
|
||||||
preferenceItems = listOf(
|
preferenceItems = listOf(
|
||||||
|
|
|
@ -103,6 +103,7 @@ object SettingsDownloadScreen : SearchableSettings {
|
||||||
}
|
}
|
||||||
|
|
||||||
val defaultDirPair = rememberDefaultDownloadDir()
|
val defaultDirPair = rememberDefaultDownloadDir()
|
||||||
|
val externalDownloaderDirPair = rememberExternalDownloaderDownloadDir()
|
||||||
val customDirEntryKey = currentDir.takeIf { it != defaultDirPair.first } ?: "custom"
|
val customDirEntryKey = currentDir.takeIf { it != defaultDirPair.first } ?: "custom"
|
||||||
|
|
||||||
return Preference.PreferenceItem.ListPreference(
|
return Preference.PreferenceItem.ListPreference(
|
||||||
|
@ -115,6 +116,7 @@ object SettingsDownloadScreen : SearchableSettings {
|
||||||
},
|
},
|
||||||
entries = mapOf(
|
entries = mapOf(
|
||||||
defaultDirPair,
|
defaultDirPair,
|
||||||
|
externalDownloaderDirPair,
|
||||||
customDirEntryKey to stringResource(R.string.custom_dir),
|
customDirEntryKey to stringResource(R.string.custom_dir),
|
||||||
),
|
),
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
|
@ -129,6 +131,20 @@ object SettingsDownloadScreen : SearchableSettings {
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun rememberDefaultDownloadDir(): Pair<String, String> {
|
private fun rememberDefaultDownloadDir(): Pair<String, String> {
|
||||||
|
val appName = stringResource(R.string.app_name)
|
||||||
|
return remember {
|
||||||
|
val file = UniFile.fromFile(
|
||||||
|
File(
|
||||||
|
"${Environment.getExternalStorageDirectory().absolutePath}${File.separator}$appName",
|
||||||
|
"downloads",
|
||||||
|
),
|
||||||
|
)!!
|
||||||
|
file.uri.toString() + "\n" to file.filePath!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun rememberExternalDownloaderDownloadDir(): Pair<String, String> {
|
||||||
val appName = stringResource(R.string.app_name)
|
val appName = stringResource(R.string.app_name)
|
||||||
return remember {
|
return remember {
|
||||||
val file = UniFile.fromFile(
|
val file = UniFile.fromFile(
|
||||||
|
|
|
@ -35,8 +35,8 @@ import androidx.core.content.ContextCompat
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import cafe.adriel.voyager.navigator.Navigator
|
import cafe.adriel.voyager.navigator.Navigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
import eu.kanade.domain.category.interactor.GetAnimeCategories
|
|
||||||
import com.commandiron.wheel_picker_compose.WheelPicker
|
import com.commandiron.wheel_picker_compose.WheelPicker
|
||||||
|
import eu.kanade.domain.category.interactor.GetAnimeCategories
|
||||||
import eu.kanade.domain.category.interactor.GetCategories
|
import eu.kanade.domain.category.interactor.GetCategories
|
||||||
import eu.kanade.domain.category.interactor.ResetCategoryFlags
|
import eu.kanade.domain.category.interactor.ResetCategoryFlags
|
||||||
import eu.kanade.domain.category.model.Category
|
import eu.kanade.domain.category.model.Category
|
||||||
|
@ -55,8 +55,7 @@ import eu.kanade.tachiyomi.data.preference.MANGA_HAS_UNREAD
|
||||||
import eu.kanade.tachiyomi.data.preference.MANGA_NON_COMPLETED
|
import eu.kanade.tachiyomi.data.preference.MANGA_NON_COMPLETED
|
||||||
import eu.kanade.tachiyomi.data.preference.MANGA_NON_READ
|
import eu.kanade.tachiyomi.data.preference.MANGA_NON_READ
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
import eu.kanade.tachiyomi.ui.animecategory.AnimeCategoryScreen
|
import eu.kanade.tachiyomi.ui.category.CategoriesTab
|
||||||
import eu.kanade.tachiyomi.ui.category.CategoryScreen
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
|
@ -155,7 +154,7 @@ object SettingsLibraryScreen : SearchableSettings {
|
||||||
count = userAnimeCategoriesCount,
|
count = userAnimeCategoriesCount,
|
||||||
userAnimeCategoriesCount,
|
userAnimeCategoriesCount,
|
||||||
),
|
),
|
||||||
onClick = { navigator.push(AnimeCategoryScreen()) },
|
onClick = { navigator.push(CategoriesTab(false)) },
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = libraryPreferences.defaultAnimeCategory(),
|
pref = libraryPreferences.defaultAnimeCategory(),
|
||||||
|
@ -170,7 +169,7 @@ object SettingsLibraryScreen : SearchableSettings {
|
||||||
count = userCategoriesCount,
|
count = userCategoriesCount,
|
||||||
userCategoriesCount,
|
userCategoriesCount,
|
||||||
),
|
),
|
||||||
onClick = { navigator.push(CategoryScreen()) },
|
onClick = { navigator.push(CategoriesTab(true)) },
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = libraryPreferences.defaultCategory(),
|
pref = libraryPreferences.defaultCategory(),
|
||||||
|
|
|
@ -6,10 +6,7 @@ import android.os.Build
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.LocalTextStyle
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
@ -23,14 +20,13 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clipToBounds
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.chargemap.compose.numberpicker.NumberPicker
|
import com.commandiron.wheel_picker_compose.WheelTextPicker
|
||||||
import eu.kanade.domain.base.BasePreferences
|
import eu.kanade.domain.base.BasePreferences
|
||||||
import eu.kanade.presentation.more.settings.Preference
|
import eu.kanade.presentation.more.settings.Preference
|
||||||
import eu.kanade.presentation.util.collectAsState
|
import eu.kanade.presentation.util.collectAsState
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.player.setting.PlayerPreferences
|
import eu.kanade.tachiyomi.ui.player.settings.PlayerPreferences
|
||||||
import eu.kanade.tachiyomi.util.preference.asState
|
import eu.kanade.tachiyomi.util.preference.asState
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
@ -124,7 +120,7 @@ object SettingsPlayerScreen : SearchableSettings {
|
||||||
@Composable
|
@Composable
|
||||||
private fun getInternalPlayerGroup(playerPreferences: PlayerPreferences, basePreferences: BasePreferences): Preference.PreferenceGroup {
|
private fun getInternalPlayerGroup(playerPreferences: PlayerPreferences, basePreferences: BasePreferences): Preference.PreferenceGroup {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val defaultSkipIntroLength by playerPreferences.skipLengthPreference().stateIn(scope).collectAsState()
|
val defaultSkipIntroLength by playerPreferences.defaultIntroLength().stateIn(scope).collectAsState()
|
||||||
val skipLengthPreference = playerPreferences.skipLengthPreference()
|
val skipLengthPreference = playerPreferences.skipLengthPreference()
|
||||||
val playerSmoothSeek = playerPreferences.playerSmoothSeek()
|
val playerSmoothSeek = playerPreferences.playerSmoothSeek()
|
||||||
val playerFullscreen = playerPreferences.playerFullscreen()
|
val playerFullscreen = playerPreferences.playerFullscreen()
|
||||||
|
@ -142,7 +138,7 @@ object SettingsPlayerScreen : SearchableSettings {
|
||||||
initialSkipIntroLength = defaultSkipIntroLength,
|
initialSkipIntroLength = defaultSkipIntroLength,
|
||||||
onDismissRequest = { showDialog = false },
|
onDismissRequest = { showDialog = false },
|
||||||
onValueChanged = { skipIntroLength ->
|
onValueChanged = { skipIntroLength ->
|
||||||
playerPreferences.skipLengthPreference().set(skipIntroLength)
|
playerPreferences.defaultIntroLength().set(skipIntroLength)
|
||||||
showDialog = false
|
showDialog = false
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -290,15 +286,14 @@ object SettingsPlayerScreen : SearchableSettings {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: chnage to new wheel Picker
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SkipIntroLengthDialog(
|
private fun SkipIntroLengthDialog(
|
||||||
initialSkipIntroLength: Int,
|
initialSkipIntroLength: Int,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onValueChanged: (skipIntroLength: Int) -> Unit,
|
onValueChanged: (skipIntroLength: Int) -> Unit,
|
||||||
) {
|
) {
|
||||||
var skipIntroLengthValue by rememberSaveable { mutableStateOf(initialSkipIntroLength) }
|
val skipIntroLengthValue by rememberSaveable { mutableStateOf(initialSkipIntroLength) }
|
||||||
|
var newLength = 0
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
title = { Text(text = stringResource(R.string.pref_intro_length)) },
|
title = { Text(text = stringResource(R.string.pref_intro_length)) },
|
||||||
|
@ -308,16 +303,13 @@ object SettingsPlayerScreen : SearchableSettings {
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
NumberPicker(
|
WheelTextPicker(
|
||||||
modifier = Modifier
|
texts = remember { 1..255 }.map { "$it" },
|
||||||
.fillMaxWidth()
|
onScrollFinished = {
|
||||||
.clipToBounds(),
|
newLength = it
|
||||||
value = skipIntroLengthValue,
|
null
|
||||||
onValueChange = { skipIntroLengthValue = it },
|
},
|
||||||
range = 1..255,
|
startIndex = skipIntroLengthValue,
|
||||||
label = { it.toString() },
|
|
||||||
dividersColor = MaterialTheme.colorScheme.primary,
|
|
||||||
textStyle = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.onSurface),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,7 +320,7 @@ object SettingsPlayerScreen : SearchableSettings {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(onClick = { onValueChanged(skipIntroLengthValue) }) {
|
TextButton(onClick = { onValueChanged(newLength) }) {
|
||||||
Text(text = stringResource(android.R.string.ok))
|
Text(text = stringResource(android.R.string.ok))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
package eu.kanade.presentation.more.stats
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.CollectionsBookmark
|
||||||
|
import androidx.compose.material.icons.outlined.LocalLibrary
|
||||||
|
import androidx.compose.material.icons.outlined.Schedule
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import eu.kanade.core.util.toDurationString
|
||||||
|
import eu.kanade.presentation.components.LazyColumn
|
||||||
|
import eu.kanade.presentation.more.stats.components.StatsItem
|
||||||
|
import eu.kanade.presentation.more.stats.components.StatsOverviewItem
|
||||||
|
import eu.kanade.presentation.more.stats.components.StatsSection
|
||||||
|
import eu.kanade.presentation.more.stats.data.StatsData
|
||||||
|
import eu.kanade.presentation.util.padding
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import java.util.Locale
|
||||||
|
import kotlin.time.DurationUnit
|
||||||
|
import kotlin.time.toDuration
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AnimeStatsScreenContent(
|
||||||
|
state: StatsScreenState.SuccessAnime,
|
||||||
|
paddingValues: PaddingValues,
|
||||||
|
) {
|
||||||
|
val statListState = rememberLazyListState()
|
||||||
|
LazyColumn(
|
||||||
|
state = statListState,
|
||||||
|
contentPadding = paddingValues,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
OverviewSection(state.overview)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
TitlesStats(state.titles)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
EpisodeStats(state.episodes)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
TrackerStats(state.trackers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun OverviewSection(
|
||||||
|
data: StatsData.AnimeOverview,
|
||||||
|
) {
|
||||||
|
val none = stringResource(R.string.none)
|
||||||
|
val context = LocalContext.current
|
||||||
|
val readDurationString = remember(data.totalSeenDuration) {
|
||||||
|
data.totalSeenDuration
|
||||||
|
.toDuration(DurationUnit.MILLISECONDS)
|
||||||
|
.toDurationString(context, fallback = none)
|
||||||
|
}
|
||||||
|
StatsSection(R.string.label_overview_section) {
|
||||||
|
Row {
|
||||||
|
StatsOverviewItem(
|
||||||
|
title = data.libraryAnimeCount.toString(),
|
||||||
|
subtitle = stringResource(R.string.in_library),
|
||||||
|
icon = Icons.Outlined.CollectionsBookmark,
|
||||||
|
)
|
||||||
|
StatsOverviewItem(
|
||||||
|
title = data.completedAnimeCount.toString(),
|
||||||
|
subtitle = stringResource(R.string.label_completed_titles),
|
||||||
|
icon = Icons.Outlined.LocalLibrary,
|
||||||
|
)
|
||||||
|
StatsOverviewItem(
|
||||||
|
title = readDurationString,
|
||||||
|
subtitle = stringResource(R.string.label_watched_duration),
|
||||||
|
icon = Icons.Outlined.Schedule,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TitlesStats(
|
||||||
|
data: StatsData.AnimeTitles,
|
||||||
|
) {
|
||||||
|
StatsSection(R.string.label_titles_section) {
|
||||||
|
Row {
|
||||||
|
StatsItem(
|
||||||
|
data.globalUpdateItemCount.toString(),
|
||||||
|
stringResource(R.string.label_titles_in_global_update),
|
||||||
|
)
|
||||||
|
StatsItem(
|
||||||
|
data.startedAnimeCount.toString(),
|
||||||
|
stringResource(R.string.label_started),
|
||||||
|
)
|
||||||
|
StatsItem(
|
||||||
|
data.localAnimeCount.toString(),
|
||||||
|
stringResource(R.string.label_local),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun EpisodeStats(
|
||||||
|
data: StatsData.Episodes,
|
||||||
|
) {
|
||||||
|
StatsSection(R.string.episodes) {
|
||||||
|
Row {
|
||||||
|
StatsItem(
|
||||||
|
data.totalEpisodeCount.toString(),
|
||||||
|
stringResource(R.string.label_total_chapters),
|
||||||
|
)
|
||||||
|
StatsItem(
|
||||||
|
data.readEpisodeCount.toString(),
|
||||||
|
stringResource(R.string.label_watched_episodes),
|
||||||
|
)
|
||||||
|
StatsItem(
|
||||||
|
data.downloadCount.toString(),
|
||||||
|
stringResource(R.string.label_downloaded),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TrackerStats(
|
||||||
|
data: StatsData.Trackers,
|
||||||
|
) {
|
||||||
|
val notApplicable = stringResource(R.string.not_applicable)
|
||||||
|
val meanScoreStr = remember(data.trackedTitleCount, data.meanScore) {
|
||||||
|
if (data.trackedTitleCount > 0 && !data.meanScore.isNaN()) {
|
||||||
|
// All other numbers are localized in English
|
||||||
|
String.format(Locale.ENGLISH, "%.2f ★", data.meanScore)
|
||||||
|
} else {
|
||||||
|
notApplicable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StatsSection(R.string.label_tracker_section) {
|
||||||
|
Row {
|
||||||
|
StatsItem(
|
||||||
|
data.trackedTitleCount.toString(),
|
||||||
|
stringResource(R.string.label_tracked_titles),
|
||||||
|
)
|
||||||
|
StatsItem(
|
||||||
|
meanScoreStr,
|
||||||
|
stringResource(R.string.label_mean_score),
|
||||||
|
)
|
||||||
|
StatsItem(
|
||||||
|
data.trackerCount.toString(),
|
||||||
|
stringResource(R.string.label_used),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,8 +26,8 @@ import kotlin.time.DurationUnit
|
||||||
import kotlin.time.toDuration
|
import kotlin.time.toDuration
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun StatsScreenContent(
|
fun MangaStatsScreenContent(
|
||||||
state: StatsScreenState.Success,
|
state: StatsScreenState.SuccessManga,
|
||||||
paddingValues: PaddingValues,
|
paddingValues: PaddingValues,
|
||||||
) {
|
) {
|
||||||
val statListState = rememberLazyListState()
|
val statListState = rememberLazyListState()
|
||||||
|
@ -53,7 +53,7 @@ fun StatsScreenContent(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun OverviewSection(
|
private fun OverviewSection(
|
||||||
data: StatsData.Overview,
|
data: StatsData.MangaOverview,
|
||||||
) {
|
) {
|
||||||
val none = stringResource(R.string.none)
|
val none = stringResource(R.string.none)
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
@ -85,7 +85,7 @@ private fun OverviewSection(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TitlesStats(
|
private fun TitlesStats(
|
||||||
data: StatsData.Titles,
|
data: StatsData.MangaTitles,
|
||||||
) {
|
) {
|
||||||
StatsSection(R.string.label_titles_section) {
|
StatsSection(R.string.label_titles_section) {
|
||||||
Row {
|
Row {
|
|
@ -8,10 +8,18 @@ sealed class StatsScreenState {
|
||||||
object Loading : StatsScreenState()
|
object Loading : StatsScreenState()
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class Success(
|
data class SuccessManga(
|
||||||
val overview: StatsData.Overview,
|
val overview: StatsData.MangaOverview,
|
||||||
val titles: StatsData.Titles,
|
val titles: StatsData.MangaTitles,
|
||||||
val chapters: StatsData.Chapters,
|
val chapters: StatsData.Chapters,
|
||||||
val trackers: StatsData.Trackers,
|
val trackers: StatsData.Trackers,
|
||||||
) : StatsScreenState()
|
) : StatsScreenState()
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class SuccessAnime(
|
||||||
|
val overview: StatsData.AnimeOverview,
|
||||||
|
val titles: StatsData.AnimeTitles,
|
||||||
|
val episodes: StatsData.Episodes,
|
||||||
|
val trackers: StatsData.Trackers,
|
||||||
|
) : StatsScreenState()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,24 +2,42 @@ package eu.kanade.presentation.more.stats.data
|
||||||
|
|
||||||
sealed class StatsData {
|
sealed class StatsData {
|
||||||
|
|
||||||
data class Overview(
|
data class MangaOverview(
|
||||||
val libraryMangaCount: Int,
|
val libraryMangaCount: Int,
|
||||||
val completedMangaCount: Int,
|
val completedMangaCount: Int,
|
||||||
val totalReadDuration: Long,
|
val totalReadDuration: Long,
|
||||||
) : StatsData()
|
) : StatsData()
|
||||||
|
|
||||||
data class Titles(
|
data class AnimeOverview(
|
||||||
|
val libraryAnimeCount: Int,
|
||||||
|
val completedAnimeCount: Int,
|
||||||
|
val totalSeenDuration: Long,
|
||||||
|
) : StatsData()
|
||||||
|
|
||||||
|
data class MangaTitles(
|
||||||
val globalUpdateItemCount: Int,
|
val globalUpdateItemCount: Int,
|
||||||
val startedMangaCount: Int,
|
val startedMangaCount: Int,
|
||||||
val localMangaCount: Int,
|
val localMangaCount: Int,
|
||||||
) : StatsData()
|
) : StatsData()
|
||||||
|
|
||||||
|
data class AnimeTitles(
|
||||||
|
val globalUpdateItemCount: Int,
|
||||||
|
val startedAnimeCount: Int,
|
||||||
|
val localAnimeCount: Int,
|
||||||
|
) : StatsData()
|
||||||
|
|
||||||
data class Chapters(
|
data class Chapters(
|
||||||
val totalChapterCount: Int,
|
val totalChapterCount: Int,
|
||||||
val readChapterCount: Int,
|
val readChapterCount: Int,
|
||||||
val downloadCount: Int,
|
val downloadCount: Int,
|
||||||
) : StatsData()
|
) : StatsData()
|
||||||
|
|
||||||
|
data class Episodes(
|
||||||
|
val totalEpisodeCount: Int,
|
||||||
|
val readEpisodeCount: Int,
|
||||||
|
val downloadCount: Int,
|
||||||
|
) : StatsData()
|
||||||
|
|
||||||
data class Trackers(
|
data class Trackers(
|
||||||
val trackedTitleCount: Int,
|
val trackedTitleCount: Int,
|
||||||
val meanScore: Double,
|
val meanScore: Double,
|
||||||
|
|
|
@ -1,17 +1,11 @@
|
||||||
package eu.kanade.presentation.updates
|
package eu.kanade.presentation.updates
|
||||||
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.outlined.FlipToBack
|
|
||||||
import androidx.compose.material.icons.outlined.Refresh
|
|
||||||
import androidx.compose.material.icons.outlined.SelectAll
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
@ -31,8 +25,8 @@ import eu.kanade.presentation.components.PullRefresh
|
||||||
import eu.kanade.presentation.components.Scaffold
|
import eu.kanade.presentation.components.Scaffold
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
import eu.kanade.tachiyomi.ui.updates.UpdatesItem
|
import eu.kanade.tachiyomi.ui.updates.manga.UpdatesItem
|
||||||
import eu.kanade.tachiyomi.ui.updates.UpdatesState
|
import eu.kanade.tachiyomi.ui.updates.manga.UpdatesState
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
@ -41,6 +35,7 @@ import kotlin.time.Duration.Companion.seconds
|
||||||
fun UpdateScreen(
|
fun UpdateScreen(
|
||||||
state: UpdatesState,
|
state: UpdatesState,
|
||||||
snackbarHostState: SnackbarHostState,
|
snackbarHostState: SnackbarHostState,
|
||||||
|
contentPadding: PaddingValues,
|
||||||
lastUpdated: Long,
|
lastUpdated: Long,
|
||||||
relativeTime: Int,
|
relativeTime: Int,
|
||||||
onClickCover: (UpdatesItem) -> Unit,
|
onClickCover: (UpdatesItem) -> Unit,
|
||||||
|
@ -69,7 +64,7 @@ fun UpdateScreen(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||||
) { contentPadding ->
|
) {
|
||||||
when {
|
when {
|
||||||
state.isLoading -> LoadingScreen(modifier = Modifier.padding(contentPadding))
|
state.isLoading -> LoadingScreen(modifier = Modifier.padding(contentPadding))
|
||||||
state.items.isEmpty() -> EmptyScreen(
|
state.items.isEmpty() -> EmptyScreen(
|
||||||
|
|
|
@ -43,7 +43,7 @@ import eu.kanade.presentation.util.padding
|
||||||
import eu.kanade.presentation.util.selectedBackground
|
import eu.kanade.presentation.util.selectedBackground
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
import eu.kanade.tachiyomi.ui.updates.UpdatesItem
|
import eu.kanade.tachiyomi.ui.updates.manga.UpdatesItem
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import kotlin.time.Duration.Companion.minutes
|
import kotlin.time.Duration.Companion.minutes
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,8 @@ import eu.kanade.tachiyomi.network.JavaScriptEngine
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.network.NetworkPreferences
|
import eu.kanade.tachiyomi.network.NetworkPreferences
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.ui.player.setting.PlayerPreferences
|
import eu.kanade.tachiyomi.ui.player.ExternalIntents
|
||||||
|
import eu.kanade.tachiyomi.ui.player.settings.PlayerPreferences
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||||
import eu.kanade.tachiyomi.util.system.isDevFlavor
|
import eu.kanade.tachiyomi.util.system.isDevFlavor
|
||||||
import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory
|
import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory
|
||||||
|
@ -194,6 +195,8 @@ class AppModule(val app: Application) : InjektModule {
|
||||||
|
|
||||||
addSingletonFactory { ImageSaver(app) }
|
addSingletonFactory { ImageSaver(app) }
|
||||||
|
|
||||||
|
addSingletonFactory { ExternalIntents() }
|
||||||
|
|
||||||
// Asynchronously init expensive components for a faster cold start
|
// Asynchronously init expensive components for a faster cold start
|
||||||
ContextCompat.getMainExecutor(app).execute {
|
ContextCompat.getMainExecutor(app).execute {
|
||||||
get<NetworkHelper>()
|
get<NetworkHelper>()
|
||||||
|
|
|
@ -22,7 +22,7 @@ import eu.kanade.tachiyomi.data.updater.AppUpdateJob
|
||||||
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
|
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
|
||||||
import eu.kanade.tachiyomi.network.NetworkPreferences
|
import eu.kanade.tachiyomi.network.NetworkPreferences
|
||||||
import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
|
import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
|
||||||
import eu.kanade.tachiyomi.ui.player.setting.PlayerPreferences
|
import eu.kanade.tachiyomi.ui.player.settings.PlayerPreferences
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
|
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||||
import eu.kanade.tachiyomi.util.preference.minusAssign
|
import eu.kanade.tachiyomi.util.preference.minusAssign
|
||||||
|
|
|
@ -60,7 +60,6 @@ class AnimeDownloadCache(
|
||||||
.onStart { emit(Unit) }
|
.onStart { emit(Unit) }
|
||||||
.shareIn(scope, SharingStarted.Eagerly, 1)
|
.shareIn(scope, SharingStarted.Eagerly, 1)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The interval after which this cache should be invalidated. 1 hour shouldn't cause major
|
* The interval after which this cache should be invalidated. 1 hour shouldn't cause major
|
||||||
* issues, as the cache is only used for UI feedback.
|
* issues, as the cache is only used for UI feedback.
|
||||||
|
@ -272,38 +271,38 @@ class AnimeDownloadCache(
|
||||||
}?.id
|
}?.id
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceDirs.values
|
sourceDirs.values
|
||||||
.map { sourceDir ->
|
.map { sourceDir ->
|
||||||
async {
|
async {
|
||||||
val animeDirs = sourceDir.dir.listFiles().orEmpty()
|
val animeDirs = sourceDir.dir.listFiles().orEmpty()
|
||||||
.filterNot { it.name.isNullOrBlank() }
|
.filterNot { it.name.isNullOrBlank() }
|
||||||
.associate { it.name!! to AnimeDirectory(it) }
|
.associate { it.name!! to AnimeDirectory(it) }
|
||||||
|
|
||||||
sourceDir.animeDirs = ConcurrentHashMap(animeDirs)
|
sourceDir.animeDirs = ConcurrentHashMap(animeDirs)
|
||||||
|
|
||||||
animeDirs.values.forEach { animeDir ->
|
animeDirs.values.forEach { animeDir ->
|
||||||
val episodeDirs = animeDir.dir.listFiles().orEmpty()
|
val episodeDirs = animeDir.dir.listFiles().orEmpty()
|
||||||
.mapNotNull {
|
.mapNotNull {
|
||||||
when {
|
when {
|
||||||
// Ignore incomplete downloads
|
// Ignore incomplete downloads
|
||||||
it.name?.endsWith(AnimeDownloader.TMP_DIR_SUFFIX) == true -> null
|
it.name?.endsWith(AnimeDownloader.TMP_DIR_SUFFIX) == true -> null
|
||||||
// Folder of images
|
// Folder of images
|
||||||
it.isDirectory -> it.name
|
it.isDirectory -> it.name
|
||||||
// MP4 files
|
// MP4 files
|
||||||
it.isFile && it.name?.endsWith(".mp4") == true -> it.name!!.substringBeforeLast(".mp4")
|
it.isFile && it.name?.endsWith(".mp4") == true -> it.name!!.substringBeforeLast(".mp4")
|
||||||
// MKV files
|
// MKV files
|
||||||
it.isFile && it.name?.endsWith(".mkv") == true -> it.name!!.substringBeforeLast(".mkv")
|
it.isFile && it.name?.endsWith(".mkv") == true -> it.name!!.substringBeforeLast(".mkv")
|
||||||
// Anything else is irrelevant
|
// Anything else is irrelevant
|
||||||
else -> null
|
else -> null
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.toMutableSet()
|
}
|
||||||
|
.toMutableSet()
|
||||||
|
|
||||||
animeDir.episodeDirs = episodeDirs
|
animeDir.episodeDirs = episodeDirs
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.awaitAll()
|
}
|
||||||
|
.awaitAll()
|
||||||
}.also {
|
}.also {
|
||||||
it.invokeOnCompletion(onCancelling = true) { exception ->
|
it.invokeOnCompletion(onCancelling = true) { exception ->
|
||||||
if (exception != null && exception !is CancellationException) {
|
if (exception != null && exception !is CancellationException) {
|
||||||
|
|
|
@ -93,7 +93,6 @@ class AnimeDownloadManager(
|
||||||
return queue.find { it.episode.id == episodeId }
|
return queue.find { it.episode.id == episodeId }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun startDownloadNow(episodeId: Long?) {
|
fun startDownloadNow(episodeId: Long?) {
|
||||||
if (episodeId == null) return
|
if (episodeId == null) return
|
||||||
val download = getQueuedDownloadOrNull(episodeId)
|
val download = getQueuedDownloadOrNull(episodeId)
|
||||||
|
|
|
@ -33,7 +33,6 @@ import eu.kanade.tachiyomi.util.lang.launchNow
|
||||||
import eu.kanade.tachiyomi.util.lang.plusAssign
|
import eu.kanade.tachiyomi.util.lang.plusAssign
|
||||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil.NOMEDIA_FILE
|
|
||||||
import eu.kanade.tachiyomi.util.storage.saveTo
|
import eu.kanade.tachiyomi.util.storage.saveTo
|
||||||
import eu.kanade.tachiyomi.util.storage.toFFmpegString
|
import eu.kanade.tachiyomi.util.storage.toFFmpegString
|
||||||
import eu.kanade.tachiyomi.util.system.ImageUtil
|
import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||||
|
@ -346,7 +345,7 @@ class AnimeDownloader(
|
||||||
|
|
||||||
val videoObservable = if (download.video == null) {
|
val videoObservable = if (download.video == null) {
|
||||||
// Pull video from network and add them to download object
|
// Pull video from network and add them to download object
|
||||||
download.source.fetchVideoList(download.episode).map { it.first() }
|
download.source.fetchVideoList(download.episode.toSEpisode()).map { it.first() }
|
||||||
.doOnNext { video ->
|
.doOnNext { video ->
|
||||||
if (video == null) {
|
if (video == null) {
|
||||||
throw Exception(context.getString(R.string.video_list_empty_error))
|
throw Exception(context.getString(R.string.video_list_empty_error))
|
||||||
|
@ -458,13 +457,13 @@ class AnimeDownloader(
|
||||||
video.videoUrl = file.uri.path
|
video.videoUrl = file.uri.path
|
||||||
video.progress = 100
|
video.progress = 100
|
||||||
download.downloadedImages++
|
download.downloadedImages++
|
||||||
video.status = Video.READY
|
video.status = Video.State.READY
|
||||||
}
|
}
|
||||||
.map { video }
|
.map { video }
|
||||||
// Mark this video as error and allow to download the remaining
|
// Mark this video as error and allow to download the remaining
|
||||||
.onErrorReturn {
|
.onErrorReturn {
|
||||||
video.progress = 0
|
video.progress = 0
|
||||||
video.status = Video.ERROR
|
video.status = Video.State.ERROR
|
||||||
notifier.onError(it.message, download.episode.name, download.anime.title)
|
notifier.onError(it.message, download.episode.name, download.anime.title)
|
||||||
video
|
video
|
||||||
}
|
}
|
||||||
|
@ -479,7 +478,7 @@ class AnimeDownloader(
|
||||||
* @param filename the filename of the video.
|
* @param filename the filename of the video.
|
||||||
*/
|
*/
|
||||||
private fun downloadVideo(video: Video, download: AnimeDownload, tmpDir: UniFile, filename: String): Observable<UniFile> {
|
private fun downloadVideo(video: Video, download: AnimeDownload, tmpDir: UniFile, filename: String): Observable<UniFile> {
|
||||||
video.status = Video.DOWNLOAD_IMAGE
|
video.status = Video.State.DOWNLOAD_IMAGE
|
||||||
video.progress = 0
|
video.progress = 0
|
||||||
var tries = 0
|
var tries = 0
|
||||||
return newObservable(video, download, tmpDir, filename)
|
return newObservable(video, download, tmpDir, filename)
|
||||||
|
@ -632,7 +631,7 @@ class AnimeDownloader(
|
||||||
* @param filename the filename of the video.
|
* @param filename the filename of the video.
|
||||||
*/
|
*/
|
||||||
private fun downloadVideoExternal(video: Video, source: AnimeHttpSource, tmpDir: UniFile, filename: String): Observable<UniFile> {
|
private fun downloadVideoExternal(video: Video, source: AnimeHttpSource, tmpDir: UniFile, filename: String): Observable<UniFile> {
|
||||||
video.status = Video.DOWNLOAD_IMAGE
|
video.status = Video.State.DOWNLOAD_IMAGE
|
||||||
video.progress = 0
|
video.progress = 0
|
||||||
return Observable.just(tmpDir.createFile("$filename.mp4")).map {
|
return Observable.just(tmpDir.createFile("$filename.mp4")).map {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupSerializer
|
import eu.kanade.tachiyomi.data.backup.models.BackupSerializer
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
|
|
|
@ -35,27 +35,27 @@ import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_PREFS
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_PREFS_MASK
|
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_PREFS_MASK
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK
|
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK_MASK
|
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK_MASK
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.Backup
|
import eu.kanade.tachiyomi.data.backup.models.Backup
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupAnime
|
import eu.kanade.tachiyomi.data.backup.models.BackupAnime
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupAnimeHistory
|
import eu.kanade.tachiyomi.data.backup.models.BackupAnimeHistory
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupAnimeSource
|
import eu.kanade.tachiyomi.data.backup.models.BackupAnimeSource
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupCategory
|
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupHistory
|
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupManga
|
import eu.kanade.tachiyomi.data.backup.models.BackupManga
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupPreference
|
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupSerializer
|
import eu.kanade.tachiyomi.data.backup.models.BackupSerializer
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupSource
|
import eu.kanade.tachiyomi.data.backup.models.BackupSource
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BooleanPreferenceValue
|
import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.FloatPreferenceValue
|
import eu.kanade.tachiyomi.data.backup.models.FloatPreferenceValue
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.IntPreferenceValue
|
import eu.kanade.tachiyomi.data.backup.models.IntPreferenceValue
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.LongPreferenceValue
|
import eu.kanade.tachiyomi.data.backup.models.LongPreferenceValue
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.StringPreferenceValue
|
import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.StringSetPreferenceValue
|
import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.backupAnimeTrackMapper
|
import eu.kanade.tachiyomi.data.backup.models.backupAnimeTrackMapper
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.backupCategoryMapper
|
import eu.kanade.tachiyomi.data.backup.models.backupCategoryMapper
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.backupChapterMapper
|
import eu.kanade.tachiyomi.data.backup.models.backupChapterMapper
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.backupEpisodeMapper
|
import eu.kanade.tachiyomi.data.backup.models.backupEpisodeMapper
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.backupTrackMapper
|
import eu.kanade.tachiyomi.data.backup.models.backupTrackMapper
|
||||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
|
|
|
@ -4,21 +4,21 @@ import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupAnime
|
import eu.kanade.tachiyomi.data.backup.models.BackupAnime
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupAnimeHistory
|
import eu.kanade.tachiyomi.data.backup.models.BackupAnimeHistory
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupAnimeSource
|
import eu.kanade.tachiyomi.data.backup.models.BackupAnimeSource
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupCategory
|
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupHistory
|
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupManga
|
import eu.kanade.tachiyomi.data.backup.models.BackupManga
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupPreference
|
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupSerializer
|
import eu.kanade.tachiyomi.data.backup.models.BackupSerializer
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupSource
|
import eu.kanade.tachiyomi.data.backup.models.BackupSource
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.BooleanPreferenceValue
|
import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.FloatPreferenceValue
|
import eu.kanade.tachiyomi.data.backup.models.FloatPreferenceValue
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.IntPreferenceValue
|
import eu.kanade.tachiyomi.data.backup.models.IntPreferenceValue
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.LongPreferenceValue
|
import eu.kanade.tachiyomi.data.backup.models.LongPreferenceValue
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.StringPreferenceValue
|
import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue
|
||||||
import eu.kanade.tachiyomi.data.backup.full.models.StringSetPreferenceValue
|
import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue
|
||||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.full.models
|
package eu.kanade.tachiyomi.data.backup.models
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.protobuf.ProtoNumber
|
import kotlinx.serialization.protobuf.ProtoNumber
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.full.models
|
package eu.kanade.tachiyomi.data.backup.models
|
||||||
|
|
||||||
import eu.kanade.domain.anime.model.Anime
|
import eu.kanade.domain.anime.model.Anime
|
||||||
import eu.kanade.tachiyomi.data.database.models.AnimeImpl
|
import eu.kanade.tachiyomi.data.database.models.AnimeImpl
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.full.models
|
package eu.kanade.tachiyomi.data.backup.models
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.protobuf.ProtoNumber
|
import kotlinx.serialization.protobuf.ProtoNumber
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.full.models
|
package eu.kanade.tachiyomi.data.backup.models
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeSource
|
import eu.kanade.tachiyomi.animesource.AnimeSource
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.full.models
|
package eu.kanade.tachiyomi.data.backup.models
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrackImpl
|
import eu.kanade.tachiyomi.data.database.models.AnimeTrackImpl
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.full.models
|
package eu.kanade.tachiyomi.data.backup.models
|
||||||
|
|
||||||
import eu.kanade.domain.category.model.Category
|
import eu.kanade.domain.category.model.Category
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.full.models
|
package eu.kanade.tachiyomi.data.backup.models
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.ChapterImpl
|
import eu.kanade.tachiyomi.data.database.models.ChapterImpl
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.full.models
|
package eu.kanade.tachiyomi.data.backup.models
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.EpisodeImpl
|
import eu.kanade.tachiyomi.data.database.models.EpisodeImpl
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.full.models
|
package eu.kanade.tachiyomi.data.backup.models
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.protobuf.ProtoNumber
|
import kotlinx.serialization.protobuf.ProtoNumber
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.full.models
|
package eu.kanade.tachiyomi.data.backup.models
|
||||||
|
|
||||||
import eu.kanade.domain.manga.model.Manga
|
import eu.kanade.domain.manga.model.Manga
|
||||||
import eu.kanade.tachiyomi.data.database.models.ChapterImpl
|
import eu.kanade.tachiyomi.data.database.models.ChapterImpl
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.full.models
|
package eu.kanade.tachiyomi.data.backup.models
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.protobuf.ProtoNumber
|
import kotlinx.serialization.protobuf.ProtoNumber
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.full.models
|
package eu.kanade.tachiyomi.data.backup.models
|
||||||
|
|
||||||
import kotlinx.serialization.Serializer
|
import kotlinx.serialization.Serializer
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.full.models
|
package eu.kanade.tachiyomi.data.backup.models
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.full.models
|
package eu.kanade.tachiyomi.data.backup.models
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.TrackImpl
|
import eu.kanade.tachiyomi.data.database.models.TrackImpl
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
|
@ -29,7 +29,6 @@ interface Anime : SAnime {
|
||||||
var skipIntroLength: Int
|
var skipIntroLength: Int
|
||||||
get() = viewer_flags and 0x000000FF
|
get() = viewer_flags and 0x000000FF
|
||||||
set(skipIntro) = setViewerFlags(skipIntro, 0x000000FF)
|
set(skipIntro) = setViewerFlags(skipIntro, 0x000000FF)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Anime.toDomainAnime(): DomainAnime? {
|
fun Anime.toDomainAnime(): DomainAnime? {
|
||||||
|
|
|
@ -4,10 +4,6 @@ import eu.kanade.domain.anime.model.Anime
|
||||||
import eu.kanade.domain.animetrack.model.AnimeTrack
|
import eu.kanade.domain.animetrack.model.AnimeTrack
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeSource
|
import eu.kanade.tachiyomi.animesource.AnimeSource
|
||||||
import eu.kanade.tachiyomi.data.track.model.AnimeTrackSearch
|
import eu.kanade.tachiyomi.data.track.model.AnimeTrackSearch
|
||||||
import eu.kanade.domain.manga.model.Manga
|
|
||||||
import eu.kanade.domain.track.model.Track
|
|
||||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
|
||||||
import eu.kanade.tachiyomi.source.Source
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An Enhanced Track Service will never prompt the user to match a manga with the remote.
|
* An Enhanced Track Service will never prompt the user to match a manga with the remote.
|
||||||
|
|
|
@ -134,5 +134,4 @@ interface MangaTrackService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,8 @@ import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import eu.kanade.domain.track.model.Track as DomainTrack
|
|
||||||
import eu.kanade.domain.animetrack.model.AnimeTrack as DomainAnimeTrack
|
import eu.kanade.domain.animetrack.model.AnimeTrack as DomainAnimeTrack
|
||||||
|
import eu.kanade.domain.track.model.Track as DomainTrack
|
||||||
|
|
||||||
class Anilist(private val context: Context, id: Long) : TrackService(id), MangaTrackService, AnimeTrackService {
|
class Anilist(private val context: Context, id: Long) : TrackService(id), MangaTrackService, AnimeTrackService {
|
||||||
|
|
||||||
|
|
|
@ -173,6 +173,7 @@ class KitsuLibAnime(obj: JsonObject, anime: JsonObject) {
|
||||||
else -> throw Exception("Unknown status")
|
else -> throw Exception("Unknown status")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class OAuth(
|
data class OAuth(
|
||||||
val access_token: String,
|
val access_token: String,
|
||||||
|
|
|
@ -47,8 +47,8 @@ import eu.kanade.data.AnimeDatabaseHandler
|
||||||
import eu.kanade.domain.anime.model.AnimeCover
|
import eu.kanade.domain.anime.model.AnimeCover
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.core.security.SecurityPreferences
|
import eu.kanade.tachiyomi.core.security.SecurityPreferences
|
||||||
import eu.kanade.tachiyomi.util.Constants
|
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||||
|
import eu.kanade.tachiyomi.util.Constants
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||||
import kotlinx.coroutines.MainScope
|
import kotlinx.coroutines.MainScope
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.ui
|
|
||||||
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import eu.kanade.domain.base.BasePreferences
|
|
||||||
import eu.kanade.tachiyomi.ui.animehistory.AnimeHistoryPresenter
|
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
|
||||||
import eu.kanade.tachiyomi.ui.history.HistoryPresenter
|
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
|
|
||||||
class HistoryTabsPresenter(
|
|
||||||
preferences: BasePreferences = Injekt.get(),
|
|
||||||
) : BasePresenter<HistoryTabsController>() {
|
|
||||||
|
|
||||||
val animeHistoryPresenter = AnimeHistoryPresenter(presenterScope, view)
|
|
||||||
val historyPresenter = HistoryPresenter(presenterScope, view)
|
|
||||||
|
|
||||||
val isDownloadOnly: Boolean by preferences.downloadedOnly().asState()
|
|
||||||
val isIncognitoMode: Boolean by preferences.incognitoMode().asState()
|
|
||||||
|
|
||||||
fun resumeLastChapterRead() {
|
|
||||||
historyPresenter.resumeLastChapterRead()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun resumeLastEpisodeSeen() {
|
|
||||||
animeHistoryPresenter.resumeLastEpisodeSeen()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.ui
|
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.view.View
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import eu.kanade.domain.library.service.LibraryPreferences
|
|
||||||
import eu.kanade.presentation.components.PagerState
|
|
||||||
import eu.kanade.presentation.components.TabbedScreen
|
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.ui.animeupdates.animeUpdatesTab
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.RootController
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.pushController
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe
|
|
||||||
import eu.kanade.tachiyomi.ui.download.anime.AnimeDownloadController
|
|
||||||
import eu.kanade.tachiyomi.ui.download.manga.DownloadController
|
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
|
|
||||||
class UpdatesTabsController : FullComposeController<UpdatesTabsPresenter>(), RootController {
|
|
||||||
|
|
||||||
override fun createPresenter() = UpdatesTabsPresenter()
|
|
||||||
|
|
||||||
private val state = PagerState(currentPage = TAB_ANIME)
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
override fun ComposeContent() {
|
|
||||||
val libraryPreferences: LibraryPreferences = Injekt.get()
|
|
||||||
val fromMore = libraryPreferences.bottomNavStyle().get() == 1
|
|
||||||
TabbedScreen(
|
|
||||||
titleRes = R.string.label_recent_updates,
|
|
||||||
tabs = listOf(
|
|
||||||
animeUpdatesTab(router, presenter.animeUpdatesPresenter, activity, fromMore),
|
|
||||||
updatesTab(router, presenter.updatesPresenter, activity, fromMore),
|
|
||||||
),
|
|
||||||
incognitoMode = presenter.isIncognitoMode,
|
|
||||||
downloadedOnlyMode = presenter.isDownloadOnly,
|
|
||||||
state = state,
|
|
||||||
)
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
(activity as? MainActivity)?.ready = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View) {
|
|
||||||
super.onViewCreated(view)
|
|
||||||
requestPermissionsSafe(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 301)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun openDownloadQueue() {
|
|
||||||
if (state.currentPage == TAB_MANGA) {
|
|
||||||
router.pushController(DownloadController())
|
|
||||||
} else {
|
|
||||||
router.pushController(AnimeDownloadController())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private const val TAB_ANIME = 0
|
|
||||||
private const val TAB_MANGA = 1
|
|
|
@ -1,28 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.ui
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import eu.kanade.domain.base.BasePreferences
|
|
||||||
import eu.kanade.tachiyomi.ui.animeupdates.AnimeUpdatesPresenter
|
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
|
||||||
import eu.kanade.tachiyomi.ui.updates.UpdatesPresenter
|
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
|
|
||||||
class UpdatesTabsPresenter(
|
|
||||||
preferences: BasePreferences = Injekt.get(),
|
|
||||||
) : BasePresenter<UpdatesTabsController>() {
|
|
||||||
|
|
||||||
val animeUpdatesPresenter = AnimeUpdatesPresenter(presenterScope, view)
|
|
||||||
val updatesPresenter = UpdatesPresenter(presenterScope, view)
|
|
||||||
|
|
||||||
val isDownloadOnly: Boolean by preferences.downloadedOnly().asState()
|
|
||||||
val isIncognitoMode: Boolean by preferences.incognitoMode().asState()
|
|
||||||
|
|
||||||
override fun onCreate(savedState: Bundle?) {
|
|
||||||
super.onCreate(savedState)
|
|
||||||
|
|
||||||
animeUpdatesPresenter.onCreate()
|
|
||||||
updatesPresenter.onCreate()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -18,7 +18,6 @@ import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import cafe.adriel.voyager.core.model.coroutineScope
|
|
||||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
import cafe.adriel.voyager.core.screen.Screen
|
||||||
import cafe.adriel.voyager.core.screen.uniqueScreenKey
|
import cafe.adriel.voyager.core.screen.uniqueScreenKey
|
||||||
|
@ -27,35 +26,35 @@ import cafe.adriel.voyager.navigator.Navigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
import com.commandiron.wheel_picker_compose.WheelTextPicker
|
import com.commandiron.wheel_picker_compose.WheelTextPicker
|
||||||
import eu.kanade.domain.anime.interactor.SetAnimeViewerFlags
|
import eu.kanade.domain.anime.interactor.SetAnimeViewerFlags
|
||||||
import eu.kanade.domain.episode.model.Episode
|
|
||||||
import eu.kanade.domain.anime.model.Anime
|
import eu.kanade.domain.anime.model.Anime
|
||||||
import eu.kanade.domain.anime.model.hasCustomCover
|
import eu.kanade.domain.anime.model.hasCustomCover
|
||||||
|
import eu.kanade.domain.episode.model.Episode
|
||||||
|
import eu.kanade.presentation.anime.AnimeScreen
|
||||||
|
import eu.kanade.presentation.anime.EpisodeSettingsDialog
|
||||||
|
import eu.kanade.presentation.anime.components.AnimeCoverDialog
|
||||||
|
import eu.kanade.presentation.anime.components.DeleteEpisodesDialog
|
||||||
import eu.kanade.presentation.components.ChangeCategoryDialog
|
import eu.kanade.presentation.components.ChangeCategoryDialog
|
||||||
import eu.kanade.presentation.components.DuplicateAnimeDialog
|
import eu.kanade.presentation.components.DuplicateAnimeDialog
|
||||||
import eu.kanade.presentation.components.LoadingScreen
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
import eu.kanade.presentation.components.NavigatorAdaptiveSheet
|
import eu.kanade.presentation.components.NavigatorAdaptiveSheet
|
||||||
import eu.kanade.presentation.anime.EpisodeSettingsDialog
|
|
||||||
import eu.kanade.presentation.manga.EditCoverAction
|
|
||||||
import eu.kanade.presentation.anime.AnimeScreen
|
|
||||||
import eu.kanade.presentation.anime.components.DeleteEpisodesDialog
|
|
||||||
import eu.kanade.presentation.manga.components.DownloadCustomAmountDialog
|
|
||||||
import eu.kanade.presentation.anime.components.AnimeCoverDialog
|
|
||||||
import eu.kanade.presentation.manga.BaseSelector
|
import eu.kanade.presentation.manga.BaseSelector
|
||||||
import eu.kanade.presentation.manga.TrackChapterSelector
|
import eu.kanade.presentation.manga.EditCoverAction
|
||||||
|
import eu.kanade.presentation.manga.components.DownloadCustomAmountDialog
|
||||||
import eu.kanade.presentation.util.AssistContentScreen
|
import eu.kanade.presentation.util.AssistContentScreen
|
||||||
import eu.kanade.presentation.util.isTabletUi
|
import eu.kanade.presentation.util.isTabletUi
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeSource
|
import eu.kanade.tachiyomi.animesource.AnimeSource
|
||||||
import eu.kanade.tachiyomi.animesource.isLocalOrStub
|
import eu.kanade.tachiyomi.animesource.isLocalOrStub
|
||||||
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.ui.anime.track.AnimeTrackInfoDialogHomeScreen
|
||||||
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateAnimeSearchScreen
|
|
||||||
import eu.kanade.tachiyomi.ui.browse.animesource.browse.BrowseAnimeSourceScreen
|
import eu.kanade.tachiyomi.ui.browse.animesource.browse.BrowseAnimeSourceScreen
|
||||||
import eu.kanade.tachiyomi.ui.browse.animesource.globalsearch.GlobalAnimeSearchScreen
|
import eu.kanade.tachiyomi.ui.browse.animesource.globalsearch.GlobalAnimeSearchScreen
|
||||||
import eu.kanade.tachiyomi.ui.animecategory.AnimeCategoryScreen
|
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateAnimeSearchScreen
|
||||||
|
import eu.kanade.tachiyomi.ui.category.CategoriesTab
|
||||||
import eu.kanade.tachiyomi.ui.home.HomeScreen
|
import eu.kanade.tachiyomi.ui.home.HomeScreen
|
||||||
import eu.kanade.tachiyomi.ui.anime.track.AnimeTrackInfoDialogHomeScreen
|
import eu.kanade.tachiyomi.ui.player.ExternalIntents
|
||||||
import eu.kanade.tachiyomi.ui.player.PlayerActivity
|
import eu.kanade.tachiyomi.ui.player.PlayerActivity
|
||||||
|
import eu.kanade.tachiyomi.ui.player.settings.PlayerPreferences
|
||||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||||
|
@ -64,10 +63,7 @@ import eu.kanade.tachiyomi.util.system.logcat
|
||||||
import eu.kanade.tachiyomi.util.system.toShareIntent
|
import eu.kanade.tachiyomi.util.system.toShareIntent
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
class AnimeScreen(
|
class AnimeScreen(
|
||||||
|
@ -116,7 +112,12 @@ class AnimeScreen(
|
||||||
snackbarHostState = screenModel.snackbarHostState,
|
snackbarHostState = screenModel.snackbarHostState,
|
||||||
isTabletUi = isTabletUi(),
|
isTabletUi = isTabletUi(),
|
||||||
onBackClicked = navigator::pop,
|
onBackClicked = navigator::pop,
|
||||||
onEpisodeClicked = { episode, alt -> openEpisode(context, episode, alt) },
|
onEpisodeClicked = { episode, alt ->
|
||||||
|
scope.launchIO {
|
||||||
|
openEpisode(context, episode, alt)
|
||||||
|
}
|
||||||
|
Unit
|
||||||
|
},
|
||||||
onDownloadEpisode = screenModel::runEpisodeDownloadActions.takeIf { !successState.source.isLocalOrStub() },
|
onDownloadEpisode = screenModel::runEpisodeDownloadActions.takeIf { !successState.source.isLocalOrStub() },
|
||||||
onAddToLibraryClicked = {
|
onAddToLibraryClicked = {
|
||||||
screenModel.toggleFavorite()
|
screenModel.toggleFavorite()
|
||||||
|
@ -128,14 +129,19 @@ class AnimeScreen(
|
||||||
onTagClicked = { scope.launch { performGenreSearch(navigator, it, screenModel.source!!) } },
|
onTagClicked = { scope.launch { performGenreSearch(navigator, it, screenModel.source!!) } },
|
||||||
onFilterButtonClicked = screenModel::showSettingsDialog,
|
onFilterButtonClicked = screenModel::showSettingsDialog,
|
||||||
onRefresh = screenModel::fetchAllFromSource,
|
onRefresh = screenModel::fetchAllFromSource,
|
||||||
onContinueWatching = { continueWatching(context, screenModel.getNextUnseenEpisode()) },
|
onContinueWatching = {
|
||||||
|
scope.launchIO {
|
||||||
|
continueWatching(context, screenModel.getNextUnseenEpisode())
|
||||||
|
}
|
||||||
|
Unit
|
||||||
|
},
|
||||||
onSearch = { query, global -> scope.launch { performSearch(navigator, query, global) } },
|
onSearch = { query, global -> scope.launch { performSearch(navigator, query, global) } },
|
||||||
onCoverClicked = screenModel::showCoverDialog,
|
onCoverClicked = screenModel::showCoverDialog,
|
||||||
onShareClicked = { shareAnime(context, screenModel.anime, screenModel.source) }.takeIf { isAnimeHttpSource },
|
onShareClicked = { shareAnime(context, screenModel.anime, screenModel.source) }.takeIf { isAnimeHttpSource },
|
||||||
onDownloadActionClicked = screenModel::runDownloadAction.takeIf { !successState.source.isLocalOrStub() },
|
onDownloadActionClicked = screenModel::runDownloadAction.takeIf { !successState.source.isLocalOrStub() },
|
||||||
onEditCategoryClicked = screenModel::promptChangeCategories.takeIf { successState.anime.favorite },
|
onEditCategoryClicked = screenModel::promptChangeCategories.takeIf { successState.anime.favorite },
|
||||||
onMigrateClicked = { navigator.push(MigrateAnimeSearchScreen(successState.anime.id)) }.takeIf { successState.anime.favorite },
|
onMigrateClicked = { navigator.push(MigrateAnimeSearchScreen(successState.anime.id)) }.takeIf { successState.anime.favorite },
|
||||||
changeAnimeSkipIntro = {screenModel::showAnimeSkipIntroDialog.takeIf { successState.anime.favorite }},
|
changeAnimeSkipIntro = { screenModel::showAnimeSkipIntroDialog.takeIf { successState.anime.favorite } },
|
||||||
onMultiBookmarkClicked = screenModel::bookmarkEpisodes,
|
onMultiBookmarkClicked = screenModel::bookmarkEpisodes,
|
||||||
onMultiMarkAsSeenClicked = screenModel::markEpisodesSeen,
|
onMultiMarkAsSeenClicked = screenModel::markEpisodesSeen,
|
||||||
onMarkPreviousAsSeenClicked = screenModel::markPreviousEpisodeSeen,
|
onMarkPreviousAsSeenClicked = screenModel::markPreviousEpisodeSeen,
|
||||||
|
@ -152,7 +158,7 @@ class AnimeScreen(
|
||||||
ChangeCategoryDialog(
|
ChangeCategoryDialog(
|
||||||
initialSelection = dialog.initialSelection,
|
initialSelection = dialog.initialSelection,
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
onEditCategories = { navigator.push(AnimeCategoryScreen()) },
|
onEditCategories = { navigator.push(CategoriesTab(false)) },
|
||||||
onConfirm = { include, _ ->
|
onConfirm = { include, _ ->
|
||||||
screenModel.moveAnimeToCategoriesAndAddToLibrary(dialog.anime, include)
|
screenModel.moveAnimeToCategoriesAndAddToLibrary(dialog.anime, include)
|
||||||
},
|
},
|
||||||
|
@ -242,13 +248,24 @@ class AnimeScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun continueWatching(context: Context, unseenEpisode: Episode?) {
|
private suspend fun continueWatching(context: Context, unseenEpisode: Episode?) {
|
||||||
if (unseenEpisode != null) openEpisode(context, unseenEpisode)
|
if (unseenEpisode != null) openEpisode(context, unseenEpisode)
|
||||||
}
|
}
|
||||||
|
private fun openEpisodeInternal(context: Context, animeId: Long, episodeId: Long) {
|
||||||
|
context.startActivity(PlayerActivity.newIntent(context, animeId, episodeId))
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: External Player support
|
private suspend fun openEpisodeExternal(context: Context, animeId: Long, episodeId: Long) {
|
||||||
private fun openEpisode(context: Context, episode: Episode, alt: Boolean = false) {
|
context.startActivity(ExternalIntents.newIntent(context, animeId, episodeId))
|
||||||
context.startActivity(PlayerActivity.newIntent(context, episode.animeId, episode.id))
|
}
|
||||||
|
|
||||||
|
private suspend fun openEpisode(context: Context, episode: Episode, altPlayer: Boolean = false) {
|
||||||
|
val playerPreferences: PlayerPreferences by injectLazy()
|
||||||
|
if (playerPreferences.alwaysUseExternalPlayer().get() != altPlayer) {
|
||||||
|
openEpisodeExternal(context, episode.animeId, episode.id)
|
||||||
|
} else {
|
||||||
|
openEpisodeInternal(context, episode.animeId, episode.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAnimeUrl(anime_: Anime?, source_: AnimeSource?): String? {
|
private fun getAnimeUrl(anime_: Anime?, source_: AnimeSource?): String? {
|
||||||
|
@ -347,6 +364,7 @@ fun ChangeIntroLength(
|
||||||
anime: Anime,
|
anime: Anime,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
) {
|
) {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
val setAnimeViewerFlags: SetAnimeViewerFlags by injectLazy()
|
val setAnimeViewerFlags: SetAnimeViewerFlags by injectLazy()
|
||||||
val titleText = R.string.action_change_intro_length
|
val titleText = R.string.action_change_intro_length
|
||||||
var newLength = 0
|
var newLength = 0
|
||||||
|
@ -364,10 +382,12 @@ fun ChangeIntroLength(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
launchIO {
|
scope.launchIO {
|
||||||
setAnimeViewerFlags.awaitSetSkipIntroLength(anime.id, newLength.toLong())
|
setAnimeViewerFlags.awaitSetSkipIntroLength(anime.id, newLength.toLong())
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
}},
|
}
|
||||||
|
Unit
|
||||||
|
},
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ import eu.kanade.domain.anime.interactor.GetDuplicateAnimelibAnime
|
||||||
import eu.kanade.domain.anime.interactor.SetAnimeEpisodeFlags
|
import eu.kanade.domain.anime.interactor.SetAnimeEpisodeFlags
|
||||||
import eu.kanade.domain.anime.interactor.UpdateAnime
|
import eu.kanade.domain.anime.interactor.UpdateAnime
|
||||||
import eu.kanade.domain.anime.model.Anime
|
import eu.kanade.domain.anime.model.Anime
|
||||||
import eu.kanade.domain.manga.model.TriStateFilter
|
|
||||||
import eu.kanade.domain.anime.model.isLocal
|
import eu.kanade.domain.anime.model.isLocal
|
||||||
import eu.kanade.domain.animetrack.interactor.GetAnimeTracks
|
import eu.kanade.domain.animetrack.interactor.GetAnimeTracks
|
||||||
import eu.kanade.domain.animetrack.model.toDbTrack
|
import eu.kanade.domain.animetrack.model.toDbTrack
|
||||||
|
@ -30,6 +29,7 @@ import eu.kanade.domain.episode.interactor.UpdateEpisode
|
||||||
import eu.kanade.domain.episode.model.Episode
|
import eu.kanade.domain.episode.model.Episode
|
||||||
import eu.kanade.domain.episode.model.EpisodeUpdate
|
import eu.kanade.domain.episode.model.EpisodeUpdate
|
||||||
import eu.kanade.domain.library.service.LibraryPreferences
|
import eu.kanade.domain.library.service.LibraryPreferences
|
||||||
|
import eu.kanade.domain.manga.model.TriStateFilter
|
||||||
import eu.kanade.domain.track.service.TrackPreferences
|
import eu.kanade.domain.track.service.TrackPreferences
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
import eu.kanade.domain.ui.UiPreferences
|
||||||
import eu.kanade.presentation.components.EpisodeDownloadAction
|
import eu.kanade.presentation.components.EpisodeDownloadAction
|
||||||
|
@ -41,8 +41,8 @@ import eu.kanade.tachiyomi.data.animedownload.AnimeDownloadCache
|
||||||
import eu.kanade.tachiyomi.data.animedownload.AnimeDownloadManager
|
import eu.kanade.tachiyomi.data.animedownload.AnimeDownloadManager
|
||||||
import eu.kanade.tachiyomi.data.animedownload.AnimeDownloadService
|
import eu.kanade.tachiyomi.data.animedownload.AnimeDownloadService
|
||||||
import eu.kanade.tachiyomi.data.animedownload.model.AnimeDownload
|
import eu.kanade.tachiyomi.data.animedownload.model.AnimeDownload
|
||||||
import eu.kanade.tachiyomi.data.track.EnhancedAnimeTrackService
|
|
||||||
import eu.kanade.tachiyomi.data.track.AnimeTrackService
|
import eu.kanade.tachiyomi.data.track.AnimeTrackService
|
||||||
|
import eu.kanade.tachiyomi.data.track.EnhancedAnimeTrackService
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
import eu.kanade.tachiyomi.data.track.TrackService
|
import eu.kanade.tachiyomi.data.track.TrackService
|
||||||
import eu.kanade.tachiyomi.network.HttpException
|
import eu.kanade.tachiyomi.network.HttpException
|
||||||
|
@ -57,7 +57,6 @@ import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||||
import eu.kanade.tachiyomi.util.removeCovers
|
import eu.kanade.tachiyomi.util.removeCovers
|
||||||
import eu.kanade.tachiyomi.util.shouldDownloadNewEpisodes
|
import eu.kanade.tachiyomi.util.shouldDownloadNewEpisodes
|
||||||
import eu.kanade.tachiyomi.util.system.logcat
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
|
@ -123,6 +122,7 @@ class AnimeInfoScreenModel(
|
||||||
|
|
||||||
private val selectedPositions: Array<Int> = arrayOf(-1, -1) // first and last selected index in list
|
private val selectedPositions: Array<Int> = arrayOf(-1, -1) // first and last selected index in list
|
||||||
private val selectedEpisodeIds: HashSet<Long> = HashSet()
|
private val selectedEpisodeIds: HashSet<Long> = HashSet()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to update the UI state only if it's currently in success state
|
* Helper function to update the UI state only if it's currently in success state
|
||||||
*/
|
*/
|
||||||
|
@ -252,11 +252,10 @@ class AnimeInfoScreenModel(
|
||||||
if (trackPreferences.trackOnAddingToLibrary().get() && loggedServices.isNotEmpty()) {
|
if (trackPreferences.trackOnAddingToLibrary().get() && loggedServices.isNotEmpty()) {
|
||||||
showTrackDialog()
|
showTrackDialog()
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update favorite status of anime, (removes / adds) anime (to / from) library.
|
* Update favorite status of anime, (removes / adds) anime (to / from) library.
|
||||||
*/
|
*/
|
||||||
|
@ -610,7 +609,6 @@ class AnimeInfoScreenModel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun runEpisodeDownloadActions(
|
fun runEpisodeDownloadActions(
|
||||||
items: List<EpisodeItem>,
|
items: List<EpisodeItem>,
|
||||||
action: EpisodeDownloadAction,
|
action: EpisodeDownloadAction,
|
||||||
|
@ -789,7 +787,7 @@ class AnimeInfoScreenModel(
|
||||||
TriStateFilter.ENABLED_NOT -> Anime.EPISODE_SHOW_NOT_BOOKMARKED
|
TriStateFilter.ENABLED_NOT -> Anime.EPISODE_SHOW_NOT_BOOKMARKED
|
||||||
}
|
}
|
||||||
|
|
||||||
coroutineScope.launchNonCancellable {
|
coroutineScope.launchNonCancellable {
|
||||||
setAnimeEpisodeFlags.awaitSetBookmarkFilter(anime, flag)
|
setAnimeEpisodeFlags.awaitSetBookmarkFilter(anime, flag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -829,7 +827,6 @@ class AnimeInfoScreenModel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun toggleSelection(
|
fun toggleSelection(
|
||||||
item: EpisodeItem,
|
item: EpisodeItem,
|
||||||
selected: Boolean,
|
selected: Boolean,
|
||||||
|
@ -841,7 +838,7 @@ class AnimeInfoScreenModel(
|
||||||
val selectedIndex = successState.processedEpisodes.indexOfFirst { it.episode.id == item.episode.id }
|
val selectedIndex = successState.processedEpisodes.indexOfFirst { it.episode.id == item.episode.id }
|
||||||
if (selectedIndex < 0) return@apply
|
if (selectedIndex < 0) return@apply
|
||||||
|
|
||||||
val selectedItem = get(selectedIndex )
|
val selectedItem = get(selectedIndex)
|
||||||
if ((selectedItem.selected && selected) || (!selectedItem.selected && !selected)) return@apply
|
if ((selectedItem.selected && selected) || (!selectedItem.selected && !selected)) return@apply
|
||||||
|
|
||||||
val firstSelection = none { it.selected }
|
val firstSelection = none { it.selected }
|
||||||
|
@ -855,10 +852,10 @@ class AnimeInfoScreenModel(
|
||||||
} else {
|
} else {
|
||||||
// Try to select the items in-between when possible
|
// Try to select the items in-between when possible
|
||||||
val range: IntRange
|
val range: IntRange
|
||||||
if (selectedIndex < selectedPositions[0]) {
|
if (selectedIndex < selectedPositions[0]) {
|
||||||
range = selectedIndex + 1 until selectedPositions[0]
|
range = selectedIndex + 1 until selectedPositions[0]
|
||||||
selectedPositions[0] = selectedIndex
|
selectedPositions[0] = selectedIndex
|
||||||
} else if (selectedIndex > selectedPositions[1]) {
|
} else if (selectedIndex > selectedPositions[1]) {
|
||||||
range = (selectedPositions[1] + 1) until selectedIndex
|
range = (selectedPositions[1] + 1) until selectedIndex
|
||||||
selectedPositions[1] = selectedIndex
|
selectedPositions[1] = selectedIndex
|
||||||
} else {
|
} else {
|
||||||
|
@ -876,15 +873,15 @@ class AnimeInfoScreenModel(
|
||||||
}
|
}
|
||||||
} else if (userSelected && !fromLongPress) {
|
} else if (userSelected && !fromLongPress) {
|
||||||
if (!selected) {
|
if (!selected) {
|
||||||
if (selectedIndex == selectedPositions[0]) {
|
if (selectedIndex == selectedPositions[0]) {
|
||||||
selectedPositions[0] = indexOfFirst { it.selected }
|
selectedPositions[0] = indexOfFirst { it.selected }
|
||||||
} else if (selectedIndex == selectedPositions[1]) {
|
} else if (selectedIndex == selectedPositions[1]) {
|
||||||
selectedPositions[1] = indexOfLast { it.selected }
|
selectedPositions[1] = indexOfLast { it.selected }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (selectedIndex < selectedPositions[0]) {
|
if (selectedIndex < selectedPositions[0]) {
|
||||||
selectedPositions[0] = selectedIndex
|
selectedPositions[0] = selectedIndex
|
||||||
} else if (selectedIndex > selectedPositions[1]) {
|
} else if (selectedIndex > selectedPositions[1]) {
|
||||||
selectedPositions[1] = selectedIndex
|
selectedPositions[1] = selectedIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,6 @@ import cafe.adriel.voyager.core.screen.Screen
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import cafe.adriel.voyager.navigator.Navigator
|
import cafe.adriel.voyager.navigator.Navigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
import eu.kanade.domain.episode.interactor.SyncEpisodesWithTrackServiceTwoWay
|
|
||||||
import eu.kanade.domain.anime.interactor.GetAnime
|
import eu.kanade.domain.anime.interactor.GetAnime
|
||||||
import eu.kanade.domain.anime.interactor.GetAnimeWithEpisodes
|
import eu.kanade.domain.anime.interactor.GetAnimeWithEpisodes
|
||||||
import eu.kanade.domain.animetrack.interactor.DeleteAnimeTrack
|
import eu.kanade.domain.animetrack.interactor.DeleteAnimeTrack
|
||||||
|
@ -45,20 +44,21 @@ import eu.kanade.domain.animetrack.interactor.GetAnimeTracks
|
||||||
import eu.kanade.domain.animetrack.interactor.InsertAnimeTrack
|
import eu.kanade.domain.animetrack.interactor.InsertAnimeTrack
|
||||||
import eu.kanade.domain.animetrack.model.toDbTrack
|
import eu.kanade.domain.animetrack.model.toDbTrack
|
||||||
import eu.kanade.domain.animetrack.model.toDomainTrack
|
import eu.kanade.domain.animetrack.model.toDomainTrack
|
||||||
|
import eu.kanade.domain.episode.interactor.SyncEpisodesWithTrackServiceTwoWay
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
import eu.kanade.domain.ui.UiPreferences
|
||||||
import eu.kanade.presentation.anime.AnimeTrackInfoDialogHome
|
import eu.kanade.presentation.anime.AnimeTrackInfoDialogHome
|
||||||
|
import eu.kanade.presentation.anime.AnimeTrackServiceSearch
|
||||||
import eu.kanade.presentation.components.AlertDialogContent
|
import eu.kanade.presentation.components.AlertDialogContent
|
||||||
import eu.kanade.presentation.manga.TrackChapterSelector
|
import eu.kanade.presentation.manga.TrackChapterSelector
|
||||||
import eu.kanade.presentation.manga.TrackDateSelector
|
import eu.kanade.presentation.manga.TrackDateSelector
|
||||||
import eu.kanade.presentation.manga.TrackScoreSelector
|
import eu.kanade.presentation.manga.TrackScoreSelector
|
||||||
import eu.kanade.presentation.anime.AnimeTrackServiceSearch
|
|
||||||
import eu.kanade.presentation.manga.TrackStatusSelector
|
import eu.kanade.presentation.manga.TrackStatusSelector
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
||||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||||
import eu.kanade.tachiyomi.data.track.EnhancedAnimeTrackService
|
import eu.kanade.tachiyomi.data.track.EnhancedAnimeTrackService
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
import eu.kanade.tachiyomi.data.track.TrackService
|
import eu.kanade.tachiyomi.data.track.TrackService
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
|
||||||
import eu.kanade.tachiyomi.data.track.model.AnimeTrackSearch
|
import eu.kanade.tachiyomi.data.track.model.AnimeTrackSearch
|
||||||
import eu.kanade.tachiyomi.util.lang.launchNonCancellable
|
import eu.kanade.tachiyomi.util.lang.launchNonCancellable
|
||||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.ui.animecategory
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
|
||||||
import cafe.adriel.voyager.core.screen.uniqueScreenKey
|
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
|
||||||
import eu.kanade.presentation.category.AnimeCategoryScreen
|
|
||||||
import eu.kanade.presentation.category.components.CategoryCreateDialog
|
|
||||||
import eu.kanade.presentation.category.components.CategoryDeleteDialog
|
|
||||||
import eu.kanade.presentation.category.components.CategoryRenameDialog
|
|
||||||
import eu.kanade.presentation.components.LoadingScreen
|
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
|
|
||||||
class AnimeCategoryScreen : Screen {
|
|
||||||
|
|
||||||
override val key = uniqueScreenKey
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
override fun Content() {
|
|
||||||
val context = LocalContext.current
|
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
|
||||||
val screenModel = rememberScreenModel { AnimeCategoryScreenModel() }
|
|
||||||
|
|
||||||
val state by screenModel.state.collectAsState()
|
|
||||||
|
|
||||||
if (state is AnimeCategoryScreenState.Loading) {
|
|
||||||
LoadingScreen()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val successState = state as AnimeCategoryScreenState.Success
|
|
||||||
|
|
||||||
AnimeCategoryScreen(
|
|
||||||
state = successState,
|
|
||||||
onClickCreate = { screenModel.showDialog(AnimeCategoryDialog.Create) },
|
|
||||||
onClickRename = { screenModel.showDialog(AnimeCategoryDialog.Rename(it)) },
|
|
||||||
onClickDelete = { screenModel.showDialog(AnimeCategoryDialog.Delete(it)) },
|
|
||||||
onClickMoveUp = screenModel::moveUp,
|
|
||||||
onClickMoveDown = screenModel::moveDown,
|
|
||||||
navigateUp = navigator::pop,
|
|
||||||
)
|
|
||||||
|
|
||||||
when (val dialog = successState.dialog) {
|
|
||||||
null -> {}
|
|
||||||
AnimeCategoryDialog.Create -> {
|
|
||||||
CategoryCreateDialog(
|
|
||||||
onDismissRequest = screenModel::dismissDialog,
|
|
||||||
onCreate = { screenModel.createCategory(it) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
is AnimeCategoryDialog.Rename -> {
|
|
||||||
CategoryRenameDialog(
|
|
||||||
onDismissRequest = screenModel::dismissDialog,
|
|
||||||
onRename = { screenModel.renameCategory(dialog.category, it) },
|
|
||||||
category = dialog.category,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
is AnimeCategoryDialog.Delete -> {
|
|
||||||
CategoryDeleteDialog(
|
|
||||||
onDismissRequest = screenModel::dismissDialog,
|
|
||||||
onDelete = { screenModel.deleteCategory(dialog.category.id) },
|
|
||||||
category = dialog.category,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
screenModel.events.collectLatest { event ->
|
|
||||||
if (event is AnimeCategoryEvent.LocalizedMessage) {
|
|
||||||
context.toast(event.stringRes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,132 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.ui.animehistory
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.compose.animation.graphics.res.animatedVectorResource
|
|
||||||
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
|
|
||||||
import androidx.compose.animation.graphics.vector.AnimatedImageVector
|
|
||||||
import androidx.compose.material3.SnackbarHostState
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
|
||||||
import cafe.adriel.voyager.navigator.Navigator
|
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
|
||||||
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
|
|
||||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
|
||||||
import eu.kanade.domain.episode.model.Episode
|
|
||||||
import eu.kanade.presentation.animehistory.AnimeHistoryScreen
|
|
||||||
import eu.kanade.presentation.history.components.HistoryDeleteAllDialog
|
|
||||||
import eu.kanade.presentation.animehistory.components.AnimeHistoryDeleteDialog
|
|
||||||
import eu.kanade.presentation.util.Tab
|
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
|
||||||
import eu.kanade.tachiyomi.ui.anime.AnimeScreen
|
|
||||||
import eu.kanade.tachiyomi.ui.player.PlayerActivity
|
|
||||||
import eu.kanade.tachiyomi.ui.player.setting.PlayerPreferences
|
|
||||||
import kotlinx.coroutines.channels.Channel
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import kotlinx.coroutines.flow.consumeAsFlow
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
|
|
||||||
object AnimeHistoryTab : Tab {
|
|
||||||
|
|
||||||
private val snackbarHostState = SnackbarHostState()
|
|
||||||
|
|
||||||
private val resumeLastEpisodeReadEvent = Channel<Unit>()
|
|
||||||
|
|
||||||
private val playerPreferences: PlayerPreferences by injectLazy()
|
|
||||||
|
|
||||||
override val options: TabOptions
|
|
||||||
@Composable
|
|
||||||
get() {
|
|
||||||
val isSelected = LocalTabNavigator.current.current.key == key
|
|
||||||
val image = AnimatedImageVector.animatedVectorResource(R.drawable.anim_history_enter)
|
|
||||||
return TabOptions(
|
|
||||||
index = 2u,
|
|
||||||
title = stringResource(R.string.label_recent_manga),
|
|
||||||
icon = rememberAnimatedVectorPainter(image, isSelected),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun onReselect(navigator: Navigator) {
|
|
||||||
resumeLastEpisodeReadEvent.send(Unit)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
override fun Content() {
|
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
|
||||||
val context = LocalContext.current
|
|
||||||
val screenModel = rememberScreenModel { AnimeHistoryScreenModel() }
|
|
||||||
val state by screenModel.state.collectAsState()
|
|
||||||
|
|
||||||
AnimeHistoryScreen(
|
|
||||||
state = state,
|
|
||||||
snackbarHostState = snackbarHostState,
|
|
||||||
onSearchQueryChange = screenModel::updateSearchQuery,
|
|
||||||
onClickCover = { navigator.push(AnimeScreen(it)) },
|
|
||||||
onClickResume = screenModel::getNextEpisodeForAnime,
|
|
||||||
onDialogChange = screenModel::setDialog,
|
|
||||||
)
|
|
||||||
|
|
||||||
val onDismissRequest = { screenModel.setDialog(null) }
|
|
||||||
when (val dialog = state.dialog) {
|
|
||||||
is AnimeHistoryScreenModel.Dialog.Delete -> {
|
|
||||||
AnimeHistoryDeleteDialog(
|
|
||||||
onDismissRequest = onDismissRequest,
|
|
||||||
onDelete = { all ->
|
|
||||||
if (all) {
|
|
||||||
screenModel.removeAllFromHistory(dialog.history.animeId)
|
|
||||||
} else {
|
|
||||||
screenModel.removeFromHistory(dialog.history)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
is AnimeHistoryScreenModel.Dialog.DeleteAll -> {
|
|
||||||
HistoryDeleteAllDialog(
|
|
||||||
onDismissRequest = onDismissRequest,
|
|
||||||
onDelete = screenModel::removeAllHistory,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
null -> {}
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(state.list) {
|
|
||||||
if (state.list != null) {
|
|
||||||
(context as? MainActivity)?.ready = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
screenModel.events.collectLatest { e ->
|
|
||||||
when (e) {
|
|
||||||
AnimeHistoryScreenModel.Event.InternalError ->
|
|
||||||
snackbarHostState.showSnackbar(context.getString(R.string.internal_error))
|
|
||||||
AnimeHistoryScreenModel.Event.HistoryCleared ->
|
|
||||||
snackbarHostState.showSnackbar(context.getString(R.string.clear_history_completed))
|
|
||||||
is AnimeHistoryScreenModel.Event.OpenEpisode -> openEpisode(context, e.episode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
resumeLastEpisodeReadEvent.consumeAsFlow().collectLatest {
|
|
||||||
openEpisode(context, screenModel.getNextEpisode())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Add external player setting
|
|
||||||
suspend fun openEpisode(context: Context, episode: Episode?) {
|
|
||||||
if (episode != null) {
|
|
||||||
val intent = PlayerActivity.newIntent(context, episode.animeId, episode.id)
|
|
||||||
context.startActivity(intent)
|
|
||||||
} else {
|
|
||||||
snackbarHostState.showSnackbar(context.getString(R.string.no_next_episode))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,33 +15,34 @@ import eu.kanade.core.util.fastFilter
|
||||||
import eu.kanade.core.util.fastFilterNot
|
import eu.kanade.core.util.fastFilterNot
|
||||||
import eu.kanade.core.util.fastMapNotNull
|
import eu.kanade.core.util.fastMapNotNull
|
||||||
import eu.kanade.core.util.fastPartition
|
import eu.kanade.core.util.fastPartition
|
||||||
|
import eu.kanade.domain.anime.interactor.GetAnimelibAnime
|
||||||
|
import eu.kanade.domain.anime.interactor.UpdateAnime
|
||||||
|
import eu.kanade.domain.anime.model.Anime
|
||||||
|
import eu.kanade.domain.anime.model.AnimeUpdate
|
||||||
|
import eu.kanade.domain.anime.model.isLocal
|
||||||
|
import eu.kanade.domain.animehistory.interactor.GetNextEpisodes
|
||||||
|
import eu.kanade.domain.animelib.model.AnimelibAnime
|
||||||
|
import eu.kanade.domain.animetrack.interactor.GetTracksPerAnime
|
||||||
import eu.kanade.domain.base.BasePreferences
|
import eu.kanade.domain.base.BasePreferences
|
||||||
import eu.kanade.domain.category.interactor.GetAnimeCategories
|
import eu.kanade.domain.category.interactor.GetAnimeCategories
|
||||||
import eu.kanade.domain.category.interactor.SetAnimeCategories
|
import eu.kanade.domain.category.interactor.SetAnimeCategories
|
||||||
import eu.kanade.domain.category.model.Category
|
import eu.kanade.domain.category.model.Category
|
||||||
import eu.kanade.domain.episode.interactor.GetEpisodeByAnimeId
|
import eu.kanade.domain.episode.interactor.GetEpisodeByAnimeId
|
||||||
import eu.kanade.domain.episode.interactor.SetSeenStatus
|
import eu.kanade.domain.episode.interactor.SetSeenStatus
|
||||||
import eu.kanade.domain.animehistory.interactor.GetNextEpisodes
|
import eu.kanade.domain.episode.model.Episode
|
||||||
import eu.kanade.domain.library.model.LibrarySort
|
import eu.kanade.domain.library.model.LibrarySort
|
||||||
import eu.kanade.domain.library.model.sort
|
import eu.kanade.domain.library.model.sort
|
||||||
import eu.kanade.domain.library.service.LibraryPreferences
|
import eu.kanade.domain.library.service.LibraryPreferences
|
||||||
import eu.kanade.domain.anime.interactor.GetAnimelibAnime
|
|
||||||
import eu.kanade.domain.anime.interactor.UpdateAnime
|
|
||||||
import eu.kanade.domain.anime.model.Anime
|
|
||||||
import eu.kanade.domain.anime.model.AnimeUpdate
|
|
||||||
import eu.kanade.domain.anime.model.isLocal
|
|
||||||
import eu.kanade.domain.animelib.model.AnimelibAnime
|
|
||||||
import eu.kanade.domain.animetrack.interactor.GetTracksPerAnime
|
|
||||||
import eu.kanade.domain.episode.model.Episode
|
|
||||||
import eu.kanade.presentation.library.components.LibraryToolbarTitle
|
import eu.kanade.presentation.library.components.LibraryToolbarTitle
|
||||||
import eu.kanade.presentation.manga.DownloadAction
|
import eu.kanade.presentation.manga.DownloadAction
|
||||||
import eu.kanade.tachiyomi.data.cache.AnimeCoverCache
|
|
||||||
import eu.kanade.tachiyomi.data.animedownload.AnimeDownloadCache
|
|
||||||
import eu.kanade.tachiyomi.data.animedownload.AnimeDownloadManager
|
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
||||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||||
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||||
|
import eu.kanade.tachiyomi.data.animedownload.AnimeDownloadCache
|
||||||
|
import eu.kanade.tachiyomi.data.animedownload.AnimeDownloadManager
|
||||||
|
import eu.kanade.tachiyomi.data.cache.AnimeCoverCache
|
||||||
|
import eu.kanade.tachiyomi.data.track.AnimeTrackService
|
||||||
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
import eu.kanade.tachiyomi.util.episode.getNextUnseen
|
import eu.kanade.tachiyomi.util.episode.getNextUnseen
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.lang.launchNonCancellable
|
import eu.kanade.tachiyomi.util.lang.launchNonCancellable
|
||||||
|
@ -337,11 +338,11 @@ class AnimelibScreenModel(
|
||||||
libraryPreferences.languageBadge().changes(),
|
libraryPreferences.languageBadge().changes(),
|
||||||
|
|
||||||
preferences.downloadedOnly().changes(),
|
preferences.downloadedOnly().changes(),
|
||||||
libraryPreferences.filterDownloaded().changes(),
|
libraryPreferences.filterDownloadedAnime().changes(),
|
||||||
libraryPreferences.filterUnread().changes(),
|
libraryPreferences.filterUnseen().changes(),
|
||||||
libraryPreferences.filterStarted().changes(),
|
libraryPreferences.filterStartedAnime().changes(),
|
||||||
libraryPreferences.filterBookmarked().changes(),
|
libraryPreferences.filterBookmarkedAnime().changes(),
|
||||||
libraryPreferences.filterCompleted().changes(),
|
libraryPreferences.filterCompletedAnime().changes(),
|
||||||
transform = {
|
transform = {
|
||||||
ItemPreferences(
|
ItemPreferences(
|
||||||
downloadBadge = it[0] as Boolean,
|
downloadBadge = it[0] as Boolean,
|
||||||
|
@ -406,10 +407,10 @@ class AnimelibScreenModel(
|
||||||
* @return map of track id with the filter value
|
* @return map of track id with the filter value
|
||||||
*/
|
*/
|
||||||
private fun getTrackingFilterFlow(): Flow<Map<Long, Int>> {
|
private fun getTrackingFilterFlow(): Flow<Map<Long, Int>> {
|
||||||
val loggedServices = trackManager.services.filter { it.isLogged }
|
val loggedServices = trackManager.services.filter { it.isLogged && it is AnimeTrackService }
|
||||||
return if (loggedServices.isNotEmpty()) {
|
return if (loggedServices.isNotEmpty()) {
|
||||||
val prefFlows = loggedServices
|
val prefFlows = loggedServices
|
||||||
.map { libraryPreferences.filterTracking(it.id.toInt()).changes() }
|
.map { libraryPreferences.filterTrackingAnime(it.id.toInt()).changes() }
|
||||||
.toTypedArray()
|
.toTypedArray()
|
||||||
combine(*prefFlows) {
|
combine(*prefFlows) {
|
||||||
loggedServices
|
loggedServices
|
||||||
|
|
|
@ -126,15 +126,15 @@ class AnimelibSettingsSheet(
|
||||||
downloaded.state = State.INCLUDE.value
|
downloaded.state = State.INCLUDE.value
|
||||||
downloaded.enabled = false
|
downloaded.enabled = false
|
||||||
} else {
|
} else {
|
||||||
downloaded.state = libraryPreferences.filterDownloaded().get()
|
downloaded.state = libraryPreferences.filterDownloadedAnime().get()
|
||||||
}
|
}
|
||||||
unseen.state = libraryPreferences.filterUnread().get()
|
unseen.state = libraryPreferences.filterUnseen().get()
|
||||||
started.state = libraryPreferences.filterStarted().get()
|
started.state = libraryPreferences.filterStartedAnime().get()
|
||||||
bookmarked.state = libraryPreferences.filterBookmarked().get()
|
bookmarked.state = libraryPreferences.filterBookmarkedAnime().get()
|
||||||
completed.state = libraryPreferences.filterCompleted().get()
|
completed.state = libraryPreferences.filterCompletedAnime().get()
|
||||||
|
|
||||||
trackFilters.forEach { trackFilter ->
|
trackFilters.forEach { trackFilter ->
|
||||||
trackFilter.value.state = libraryPreferences.filterTracking(trackFilter.key.toInt()).get()
|
trackFilter.value.state = libraryPreferences.filterTrackingAnime(trackFilter.key.toInt()).get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,15 +148,15 @@ class AnimelibSettingsSheet(
|
||||||
}
|
}
|
||||||
item.state = newState
|
item.state = newState
|
||||||
when (item) {
|
when (item) {
|
||||||
downloaded -> libraryPreferences.filterDownloaded().set(newState)
|
downloaded -> libraryPreferences.filterDownloadedAnime().set(newState)
|
||||||
unseen -> libraryPreferences.filterUnread().set(newState)
|
unseen -> libraryPreferences.filterUnseen().set(newState)
|
||||||
started -> libraryPreferences.filterStarted().set(newState)
|
started -> libraryPreferences.filterStartedAnime().set(newState)
|
||||||
bookmarked -> libraryPreferences.filterBookmarked().set(newState)
|
bookmarked -> libraryPreferences.filterBookmarkedAnime().set(newState)
|
||||||
completed -> libraryPreferences.filterCompleted().set(newState)
|
completed -> libraryPreferences.filterCompletedAnime().set(newState)
|
||||||
else -> {
|
else -> {
|
||||||
trackFilters.forEach { trackFilter ->
|
trackFilters.forEach { trackFilter ->
|
||||||
if (trackFilter.value == item) {
|
if (trackFilter.value == item) {
|
||||||
libraryPreferences.filterTracking(trackFilter.key.toInt()).set(newState)
|
libraryPreferences.filterTrackingAnime(trackFilter.key.toInt()).set(newState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package eu.kanade.tachiyomi.ui.animelib
|
package eu.kanade.tachiyomi.ui.animelib
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.graphics.res.animatedVectorResource
|
import androidx.compose.animation.graphics.res.animatedVectorResource
|
||||||
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
|
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
|
||||||
|
@ -28,31 +29,34 @@ import cafe.adriel.voyager.navigator.Navigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
|
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
|
||||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||||
import eu.kanade.domain.category.model.Category
|
|
||||||
import eu.kanade.domain.animelib.model.AnimelibAnime
|
|
||||||
import eu.kanade.domain.library.model.display
|
|
||||||
import eu.kanade.domain.library.service.LibraryPreferences
|
|
||||||
import eu.kanade.domain.anime.model.Anime
|
import eu.kanade.domain.anime.model.Anime
|
||||||
import eu.kanade.domain.anime.model.isLocal
|
import eu.kanade.domain.anime.model.isLocal
|
||||||
|
import eu.kanade.domain.animelib.model.AnimelibAnime
|
||||||
|
import eu.kanade.domain.category.model.Category
|
||||||
|
import eu.kanade.domain.episode.model.Episode
|
||||||
|
import eu.kanade.domain.library.model.display
|
||||||
|
import eu.kanade.domain.library.service.LibraryPreferences
|
||||||
|
import eu.kanade.presentation.animelib.components.AnimelibContent
|
||||||
|
import eu.kanade.presentation.components.AnimelibBottomActionMenu
|
||||||
import eu.kanade.presentation.components.ChangeCategoryDialog
|
import eu.kanade.presentation.components.ChangeCategoryDialog
|
||||||
import eu.kanade.presentation.components.DeleteAnimelibAnimeDialog
|
import eu.kanade.presentation.components.DeleteAnimelibAnimeDialog
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
import eu.kanade.presentation.components.EmptyScreenAction
|
import eu.kanade.presentation.components.EmptyScreenAction
|
||||||
import eu.kanade.presentation.components.AnimelibBottomActionMenu
|
|
||||||
import eu.kanade.presentation.components.LoadingScreen
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
import eu.kanade.presentation.components.Scaffold
|
import eu.kanade.presentation.components.Scaffold
|
||||||
import eu.kanade.presentation.animelib.components.AnimelibContent
|
|
||||||
import eu.kanade.presentation.library.components.LibraryToolbar
|
import eu.kanade.presentation.library.components.LibraryToolbar
|
||||||
import eu.kanade.presentation.manga.components.DownloadCustomAmountDialog
|
import eu.kanade.presentation.manga.components.DownloadCustomAmountDialog
|
||||||
import eu.kanade.presentation.util.Tab
|
import eu.kanade.presentation.util.Tab
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.animelib.AnimelibUpdateService
|
import eu.kanade.tachiyomi.data.animelib.AnimelibUpdateService
|
||||||
|
import eu.kanade.tachiyomi.ui.anime.AnimeScreen
|
||||||
import eu.kanade.tachiyomi.ui.browse.animesource.globalsearch.GlobalAnimeSearchScreen
|
import eu.kanade.tachiyomi.ui.browse.animesource.globalsearch.GlobalAnimeSearchScreen
|
||||||
import eu.kanade.tachiyomi.ui.category.CategoryScreen
|
import eu.kanade.tachiyomi.ui.category.CategoriesTab
|
||||||
import eu.kanade.tachiyomi.ui.home.HomeScreen
|
import eu.kanade.tachiyomi.ui.home.HomeScreen
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.ui.anime.AnimeScreen
|
import eu.kanade.tachiyomi.ui.player.ExternalIntents
|
||||||
import eu.kanade.tachiyomi.ui.player.PlayerActivity
|
import eu.kanade.tachiyomi.ui.player.PlayerActivity
|
||||||
|
import eu.kanade.tachiyomi.ui.player.settings.PlayerPreferences
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
@ -72,7 +76,7 @@ object AnimelibTab : Tab {
|
||||||
R.string.label_animelib
|
R.string.label_animelib
|
||||||
}
|
}
|
||||||
val isSelected = LocalTabNavigator.current.current.key == key
|
val isSelected = LocalTabNavigator.current.current.key == key
|
||||||
val image = AnimatedImageVector.animatedVectorResource(R.drawable.anim_library_enter)
|
val image = AnimatedImageVector.animatedVectorResource(R.drawable.anim_animelibrary_leave)
|
||||||
return TabOptions(
|
return TabOptions(
|
||||||
index = 0u,
|
index = 0u,
|
||||||
title = stringResource(title),
|
title = stringResource(title),
|
||||||
|
@ -108,6 +112,23 @@ object AnimelibTab : Tab {
|
||||||
scope.launch { sendSettingsSheetIntent(state.categories[screenModel.activeCategoryIndex]) }
|
scope.launch { sendSettingsSheetIntent(state.categories[screenModel.activeCategoryIndex]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun openEpisodeInternal(context: Context, animeId: Long, episodeId: Long) {
|
||||||
|
context.startActivity(PlayerActivity.newIntent(context, animeId, episodeId))
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun openEpisodeExternal(context: Context, animeId: Long, episodeId: Long) {
|
||||||
|
context.startActivity(ExternalIntents.newIntent(context, animeId, episodeId))
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun openEpisode(episode: Episode) {
|
||||||
|
val playerPreferences: PlayerPreferences by injectLazy()
|
||||||
|
if (playerPreferences.alwaysUseExternalPlayer().get()) {
|
||||||
|
openEpisodeExternal(context, episode.animeId, episode.id)
|
||||||
|
} else {
|
||||||
|
openEpisodeInternal(context, episode.animeId, episode.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = { scrollBehavior ->
|
topBar = { scrollBehavior ->
|
||||||
val title = state.getToolbarTitle(
|
val title = state.getToolbarTitle(
|
||||||
|
@ -183,13 +204,8 @@ object AnimelibTab : Tab {
|
||||||
onContinueWatchingClicked = { it: AnimelibAnime ->
|
onContinueWatchingClicked = { it: AnimelibAnime ->
|
||||||
scope.launchIO {
|
scope.launchIO {
|
||||||
val episode = screenModel.getNextUnseenEpisode(it.anime)
|
val episode = screenModel.getNextUnseenEpisode(it.anime)
|
||||||
if (episode != null) {
|
if (episode != null) openEpisode(episode)
|
||||||
context.startActivity(PlayerActivity.newIntent(context, episode.animeId, episode.id))
|
|
||||||
} else {
|
|
||||||
snackbarHostState.showSnackbar(context.getString(R.string.no_next_episode))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// TODO: External Intent
|
|
||||||
Unit
|
Unit
|
||||||
}.takeIf { state.showAnimeContinueButton },
|
}.takeIf { state.showAnimeContinueButton },
|
||||||
onToggleSelection = { screenModel.toggleSelection(it) },
|
onToggleSelection = { screenModel.toggleSelection(it) },
|
||||||
|
@ -217,7 +233,7 @@ object AnimelibTab : Tab {
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
onEditCategories = {
|
onEditCategories = {
|
||||||
screenModel.clearSelection()
|
screenModel.clearSelection()
|
||||||
navigator.push(CategoryScreen())
|
navigator.push(CategoriesTab(false))
|
||||||
},
|
},
|
||||||
onConfirm = { include, exclude ->
|
onConfirm = { include, exclude ->
|
||||||
screenModel.clearSelection()
|
screenModel.clearSelection()
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.ui.animeupdates
|
|
||||||
|
|
||||||
import androidx.compose.animation.graphics.res.animatedVectorResource
|
|
||||||
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
|
|
||||||
import androidx.compose.animation.graphics.vector.AnimatedImageVector
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.DisposableEffect
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
|
||||||
import cafe.adriel.voyager.navigator.Navigator
|
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
|
||||||
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
|
|
||||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
|
||||||
import eu.kanade.presentation.animeupdates.AnimeUpdateScreen
|
|
||||||
import eu.kanade.presentation.animeupdates.AnimeUpdatesDeleteConfirmationDialog
|
|
||||||
import eu.kanade.presentation.util.Tab
|
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.ui.download.anime.AnimeDownloadQueueScreen
|
|
||||||
import eu.kanade.tachiyomi.ui.home.HomeScreen
|
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
|
||||||
import eu.kanade.tachiyomi.ui.anime.AnimeScreen
|
|
||||||
import eu.kanade.tachiyomi.ui.animeupdates.AnimeUpdatesScreenModel.Event
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
|
|
||||||
object AnimeUpdatesTab : Tab {
|
|
||||||
|
|
||||||
override val options: TabOptions
|
|
||||||
@Composable
|
|
||||||
get() {
|
|
||||||
val isSelected = LocalTabNavigator.current.current.key == key
|
|
||||||
val image = AnimatedImageVector.animatedVectorResource(R.drawable.anim_updates_enter)
|
|
||||||
return TabOptions(
|
|
||||||
index = 1u,
|
|
||||||
title = stringResource(R.string.label_recent_updates),
|
|
||||||
icon = rememberAnimatedVectorPainter(image, isSelected),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun onReselect(navigator: Navigator) {
|
|
||||||
navigator.push(AnimeDownloadQueueScreen)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
override fun Content() {
|
|
||||||
val context = LocalContext.current
|
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
|
||||||
val screenModel = rememberScreenModel { AnimeUpdatesScreenModel() }
|
|
||||||
val state by screenModel.state.collectAsState()
|
|
||||||
|
|
||||||
AnimeUpdateScreen(
|
|
||||||
state = state,
|
|
||||||
snackbarHostState = screenModel.snackbarHostState,
|
|
||||||
lastUpdated = screenModel.lastUpdated,
|
|
||||||
relativeTime = screenModel.relativeTime,
|
|
||||||
onClickCover = { item -> navigator.push(AnimeScreen(item.update.animeId)) },
|
|
||||||
onSelectAll = screenModel::toggleAllSelection,
|
|
||||||
onInvertSelection = screenModel::invertSelection,
|
|
||||||
onUpdateLibrary = screenModel::updateLibrary,
|
|
||||||
onDownloadEpisode = screenModel::downloadEpisodes,
|
|
||||||
onMultiBookmarkClicked = screenModel::bookmarkUpdates,
|
|
||||||
onMultiMarkAsReadClicked = screenModel::markUpdatesSeen,
|
|
||||||
onMultiDeleteClicked = screenModel::showConfirmDeleteEpisodes,
|
|
||||||
onUpdateSelected = screenModel::toggleSelection,
|
|
||||||
onOpenEpisode = screenModel::openEpisode,
|
|
||||||
)
|
|
||||||
|
|
||||||
val onDismissDialog = { screenModel.setDialog(null) }
|
|
||||||
when (val dialog = state.dialog) {
|
|
||||||
is AnimeUpdatesScreenModel.Dialog.DeleteConfirmation -> {
|
|
||||||
AnimeUpdatesDeleteConfirmationDialog(
|
|
||||||
onDismissRequest = onDismissDialog,
|
|
||||||
onConfirm = { screenModel.deleteEpisodes(dialog.toDelete) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
null -> {}
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
screenModel.events.collectLatest { event ->
|
|
||||||
when (event) {
|
|
||||||
Event.InternalError -> screenModel.snackbarHostState.showSnackbar(context.getString(R.string.internal_error))
|
|
||||||
is Event.LibraryUpdateTriggered -> {
|
|
||||||
val msg = if (event.started) {
|
|
||||||
R.string.updating_library
|
|
||||||
} else {
|
|
||||||
R.string.update_already_running
|
|
||||||
}
|
|
||||||
screenModel.snackbarHostState.showSnackbar(context.getString(msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(state.selectionMode) {
|
|
||||||
HomeScreen.showBottomNav(!state.selectionMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(state.isLoading) {
|
|
||||||
if (!state.isLoading) {
|
|
||||||
(context as? MainActivity)?.ready = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DisposableEffect(Unit) {
|
|
||||||
screenModel.resetNewUpdatesCount()
|
|
||||||
|
|
||||||
onDispose {
|
|
||||||
screenModel.resetNewUpdatesCount()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -36,7 +36,7 @@ data class BrowseTab(
|
||||||
val isSelected = LocalTabNavigator.current.current.key == key
|
val isSelected = LocalTabNavigator.current.current.key == key
|
||||||
val image = AnimatedImageVector.animatedVectorResource(R.drawable.anim_browse_enter)
|
val image = AnimatedImageVector.animatedVectorResource(R.drawable.anim_browse_enter)
|
||||||
return TabOptions(
|
return TabOptions(
|
||||||
index = 6u,
|
index = 3u,
|
||||||
title = stringResource(R.string.browse),
|
title = stringResource(R.string.browse),
|
||||||
icon = rememberAnimatedVectorPainter(image, isSelected),
|
icon = rememberAnimatedVectorPainter(image, isSelected),
|
||||||
)
|
)
|
||||||
|
@ -68,6 +68,7 @@ data class BrowseTab(
|
||||||
onChangeSearchQuery = extensionsScreenModel::search,
|
onChangeSearchQuery = extensionsScreenModel::search,
|
||||||
searchQueryAnime = animeExtensionsQuery,
|
searchQueryAnime = animeExtensionsQuery,
|
||||||
onChangeSearchQueryAnime = animeExtensionsScreenModel::search,
|
onChangeSearchQueryAnime = animeExtensionsScreenModel::search,
|
||||||
|
scrollable = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
// For local source
|
// For local source
|
||||||
|
|
|
@ -220,7 +220,6 @@ data class AnimeExtensionsState(
|
||||||
val isEmpty = items.isEmpty()
|
val isEmpty = items.isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sealed interface AnimeExtensionUiModel {
|
sealed interface AnimeExtensionUiModel {
|
||||||
sealed interface Header : AnimeExtensionUiModel {
|
sealed interface Header : AnimeExtensionUiModel {
|
||||||
data class Resource(@StringRes val textRes: Int) : Header
|
data class Resource(@StringRes val textRes: Int) : Header
|
||||||
|
|
|
@ -21,189 +21,7 @@ data class AnimeExtensionDetailsScreen(
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val screenModel = rememberScreenModel { AnimeEpackage eu.kanade.tachiyomi.ui.browse.extension.details
|
val screenModel = rememberScreenModel { AnimeExtensionDetailsScreenModel(pkgName = pkgName, context = context) }
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.util.TypedValue
|
|
||||||
import android.view.View
|
|
||||||
import androidx.appcompat.view.ContextThemeWrapper
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.outlined.ArrowBack
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
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.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
|
||||||
import androidx.core.os.bundleOf
|
|
||||||
import androidx.fragment.app.FragmentActivity
|
|
||||||
import androidx.fragment.app.FragmentContainerView
|
|
||||||
import androidx.fragment.app.FragmentManager
|
|
||||||
import androidx.fragment.app.FragmentTransaction
|
|
||||||
import androidx.fragment.app.commit
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.preference.DialogPreference
|
|
||||||
import androidx.preference.EditTextPreference
|
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
|
||||||
import androidx.preference.PreferenceScreen
|
|
||||||
import androidx.preference.forEach
|
|
||||||
import androidx.preference.getOnBindEditTextListener
|
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
|
||||||
import cafe.adriel.voyager.core.screen.uniqueScreenKey
|
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
|
||||||
import eu.kanade.presentation.components.Scaffold
|
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.data.preference.SharedPreferencesDataStore
|
|
||||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
|
||||||
import eu.kanade.tachiyomi.source.getPreferenceKey
|
|
||||||
import eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText.Companion.setIncognito
|
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
|
|
||||||
class SourcePreferencesScreen(val sourceId: Long) : Screen {
|
|
||||||
|
|
||||||
override val key = uniqueScreenKey
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
override fun Content() {
|
|
||||||
val context = LocalContext.current
|
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
|
||||||
|
|
||||||
Scaffold(
|
|
||||||
topBar = {
|
|
||||||
TopAppBar(
|
|
||||||
title = { Text(text = Injekt.get<SourceManager>().get(sourceId)!!.toString()) },
|
|
||||||
navigationIcon = {
|
|
||||||
IconButton(onClick = navigator::pop) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Outlined.ArrowBack,
|
|
||||||
contentDescription = stringResource(R.string.abc_action_bar_up_description),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scrollBehavior = it,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
) { contentPadding ->
|
|
||||||
FragmentContainer(
|
|
||||||
fragmentManager = (context as FragmentActivity).supportFragmentManager,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(contentPadding),
|
|
||||||
) {
|
|
||||||
val fragment = SourcePreferencesFragment.getInstance(sourceId)
|
|
||||||
add(it, fragment, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* From https://stackoverflow.com/questions/60520145/fragment-container-in-jetpack-compose/70817794#70817794
|
|
||||||
*/
|
|
||||||
@Composable
|
|
||||||
private fun FragmentContainer(
|
|
||||||
fragmentManager: FragmentManager,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
commit: FragmentTransaction.(containerId: Int) -> Unit,
|
|
||||||
) {
|
|
||||||
val containerId by rememberSaveable {
|
|
||||||
mutableStateOf(View.generateViewId())
|
|
||||||
}
|
|
||||||
var initialized by rememberSaveable { mutableStateOf(false) }
|
|
||||||
AndroidView(
|
|
||||||
modifier = modifier,
|
|
||||||
factory = { context ->
|
|
||||||
FragmentContainerView(context)
|
|
||||||
.apply { id = containerId }
|
|
||||||
},
|
|
||||||
update = { view ->
|
|
||||||
if (!initialized) {
|
|
||||||
fragmentManager.commit { commit(view.id) }
|
|
||||||
initialized = true
|
|
||||||
} else {
|
|
||||||
fragmentManager.onContainerAvailable(view)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Access to package-private method in FragmentManager through reflection */
|
|
||||||
private fun FragmentManager.onContainerAvailable(view: FragmentContainerView) {
|
|
||||||
val method = FragmentManager::class.java.getDeclaredMethod(
|
|
||||||
"onContainerAvailable",
|
|
||||||
FragmentContainerView::class.java,
|
|
||||||
)
|
|
||||||
method.isAccessible = true
|
|
||||||
method.invoke(this, view)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SourcePreferencesFragment : PreferenceFragmentCompat() {
|
|
||||||
|
|
||||||
override fun getContext(): Context? {
|
|
||||||
val superCtx = super.getContext() ?: return null
|
|
||||||
val tv = TypedValue()
|
|
||||||
superCtx.theme.resolveAttribute(R.attr.preferenceTheme, tv, true)
|
|
||||||
return ContextThemeWrapper(superCtx, tv.resourceId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
||||||
preferenceScreen = populateScreen()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun populateScreen(): PreferenceScreen {
|
|
||||||
val sourceId = requireArguments().getLong(SOURCE_ID)
|
|
||||||
val source = Injekt.get<SourceManager>().get(sourceId)!!
|
|
||||||
|
|
||||||
check(source is ConfigurableSource)
|
|
||||||
|
|
||||||
val sharedPreferences = requireContext().getSharedPreferences(source.getPreferenceKey(), Context.MODE_PRIVATE)
|
|
||||||
val dataStore = SharedPreferencesDataStore(sharedPreferences)
|
|
||||||
preferenceManager.preferenceDataStore = dataStore
|
|
||||||
|
|
||||||
val sourceScreen = preferenceManager.createPreferenceScreen(requireContext())
|
|
||||||
source.setupPreferenceScreen(sourceScreen)
|
|
||||||
sourceScreen.forEach { pref ->
|
|
||||||
pref.isIconSpaceReserved = false
|
|
||||||
if (pref is DialogPreference) {
|
|
||||||
pref.dialogTitle = pref.title
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply incognito IME for EditTextPreference
|
|
||||||
if (pref is EditTextPreference) {
|
|
||||||
val setListener = pref.getOnBindEditTextListener()
|
|
||||||
pref.setOnBindEditTextListener {
|
|
||||||
setListener?.onBindEditText(it)
|
|
||||||
it.setIncognito(lifecycleScope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sourceScreen
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val SOURCE_ID = "source_id"
|
|
||||||
|
|
||||||
fun getInstance(sourceId: Long): SourcePreferencesFragment {
|
|
||||||
val fragment = SourcePreferencesFragment()
|
|
||||||
fragment.arguments = bundleOf(SOURCE_ID to sourceId)
|
|
||||||
return fragment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}xtensionDetailsScreenModel(pkgName = pkgName, context = context) }
|
|
||||||
val state by screenModel.state.collectAsState()
|
val state by screenModel.state.collectAsState()
|
||||||
|
|
||||||
if (state.isLoading) {
|
if (state.isLoading) {
|
||||||
|
|
|
@ -41,9 +41,10 @@ import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
import eu.kanade.presentation.components.Scaffold
|
import eu.kanade.presentation.components.Scaffold
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.preference.SharedPreferencesDataStore
|
|
||||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
||||||
|
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||||
|
import eu.kanade.tachiyomi.animesource.getPreferenceKey
|
||||||
|
import eu.kanade.tachiyomi.data.preference.SharedPreferencesDataStore
|
||||||
import eu.kanade.tachiyomi.source.getPreferenceKey
|
import eu.kanade.tachiyomi.source.getPreferenceKey
|
||||||
import eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText.Companion.setIncognito
|
import eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText.Companion.setIncognito
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
|
|
|
@ -3,9 +3,9 @@ package eu.kanade.tachiyomi.ui.browse.animesource
|
||||||
import cafe.adriel.voyager.core.model.StateScreenModel
|
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||||
import cafe.adriel.voyager.core.model.coroutineScope
|
import cafe.adriel.voyager.core.model.coroutineScope
|
||||||
import eu.kanade.domain.animesource.interactor.GetLanguagesWithAnimeSources
|
import eu.kanade.domain.animesource.interactor.GetLanguagesWithAnimeSources
|
||||||
import eu.kanade.domain.source.interactor.ToggleLanguage
|
|
||||||
import eu.kanade.domain.animesource.interactor.ToggleAnimeSource
|
import eu.kanade.domain.animesource.interactor.ToggleAnimeSource
|
||||||
import eu.kanade.domain.animesource.model.AnimeSource
|
import eu.kanade.domain.animesource.model.AnimeSource
|
||||||
|
import eu.kanade.domain.source.interactor.ToggleLanguage
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
@ -27,7 +27,7 @@ class AnimeSourcesFilterScreenModel(
|
||||||
combine(
|
combine(
|
||||||
getLanguagesWithSources.subscribe(),
|
getLanguagesWithSources.subscribe(),
|
||||||
preferences.enabledLanguages().changes(),
|
preferences.enabledLanguages().changes(),
|
||||||
preferences.disabledSources().changes(),
|
preferences.disabledAnimeSources().changes(),
|
||||||
) { a, b, c -> Triple(a, b, c) }
|
) { a, b, c -> Triple(a, b, c) }
|
||||||
.catch { throwable ->
|
.catch { throwable ->
|
||||||
mutableState.update {
|
mutableState.update {
|
||||||
|
|
|
@ -50,9 +50,9 @@ import eu.kanade.presentation.util.AssistContentScreen
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.animesource.LocalAnimeSource
|
import eu.kanade.tachiyomi.animesource.LocalAnimeSource
|
||||||
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||||
import eu.kanade.tachiyomi.ui.browse.animesource.browse.BrowseAnimeSourceScreenModel.Listing
|
|
||||||
import eu.kanade.tachiyomi.ui.animecategory.AnimeCategoryScreen
|
|
||||||
import eu.kanade.tachiyomi.ui.anime.AnimeScreen
|
import eu.kanade.tachiyomi.ui.anime.AnimeScreen
|
||||||
|
import eu.kanade.tachiyomi.ui.browse.animesource.browse.BrowseAnimeSourceScreenModel.Listing
|
||||||
|
import eu.kanade.tachiyomi.ui.category.CategoriesTab
|
||||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||||
import eu.kanade.tachiyomi.util.Constants
|
import eu.kanade.tachiyomi.util.Constants
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
|
@ -242,7 +242,7 @@ data class BrowseAnimeSourceScreen(
|
||||||
ChangeCategoryDialog(
|
ChangeCategoryDialog(
|
||||||
initialSelection = dialog.initialSelection,
|
initialSelection = dialog.initialSelection,
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
onEditCategories = { navigator.push(AnimeCategoryScreen()) },
|
onEditCategories = { navigator.push(CategoriesTab(false)) },
|
||||||
onConfirm = { include, _ ->
|
onConfirm = { include, _ ->
|
||||||
screenModel.changeAnimeFavorite(dialog.anime)
|
screenModel.changeAnimeFavorite(dialog.anime)
|
||||||
screenModel.moveAnimeToCategories(dialog.anime, include)
|
screenModel.moveAnimeToCategories(dialog.anime, include)
|
||||||
|
|
|
@ -21,8 +21,8 @@ import eu.kanade.domain.anime.interactor.GetAnime
|
||||||
import eu.kanade.domain.anime.interactor.GetDuplicateAnimelibAnime
|
import eu.kanade.domain.anime.interactor.GetDuplicateAnimelibAnime
|
||||||
import eu.kanade.domain.anime.interactor.NetworkToLocalAnime
|
import eu.kanade.domain.anime.interactor.NetworkToLocalAnime
|
||||||
import eu.kanade.domain.anime.interactor.UpdateAnime
|
import eu.kanade.domain.anime.interactor.UpdateAnime
|
||||||
import eu.kanade.domain.anime.model.toAnimeUpdate
|
|
||||||
import eu.kanade.domain.anime.model.Anime
|
import eu.kanade.domain.anime.model.Anime
|
||||||
|
import eu.kanade.domain.anime.model.toAnimeUpdate
|
||||||
import eu.kanade.domain.anime.model.toDomainAnime
|
import eu.kanade.domain.anime.model.toDomainAnime
|
||||||
import eu.kanade.domain.animesource.interactor.GetRemoteAnime
|
import eu.kanade.domain.animesource.interactor.GetRemoteAnime
|
||||||
import eu.kanade.domain.animetrack.interactor.InsertAnimeTrack
|
import eu.kanade.domain.animetrack.interactor.InsertAnimeTrack
|
||||||
|
@ -299,10 +299,12 @@ class BrowseAnimeSourceScreenModel(
|
||||||
// Choose a category
|
// Choose a category
|
||||||
else -> {
|
else -> {
|
||||||
val preselectedIds = getCategories.await(anime.id).map { it.id }
|
val preselectedIds = getCategories.await(anime.id).map { it.id }
|
||||||
setDialog(Dialog.ChangeAnimeCategory(
|
setDialog(
|
||||||
anime,
|
Dialog.ChangeAnimeCategory(
|
||||||
categories.mapAsCheckboxState { it.id in preselectedIds },
|
anime,
|
||||||
))
|
categories.mapAsCheckboxState { it.id in preselectedIds },
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -320,7 +322,7 @@ class BrowseAnimeSourceScreenModel(
|
||||||
insertTrack.await(track.toDomainTrack()!!)
|
insertTrack.await(track.toDomainTrack()!!)
|
||||||
|
|
||||||
val chapters = getEpisodeByAnimeId.await(anime.id)
|
val chapters = getEpisodeByAnimeId.await(anime.id)
|
||||||
syncEpisodesWithTrackServiceTwoWay.await(chapters, track.toDomainTrack()!!, service.animeService )
|
syncEpisodesWithTrackServiceTwoWay.await(chapters, track.toDomainTrack()!!, service.animeService)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logcat(
|
logcat(
|
||||||
|
|
|
@ -9,8 +9,8 @@ import eu.kanade.domain.anime.interactor.GetAnime
|
||||||
import eu.kanade.domain.anime.interactor.NetworkToLocalAnime
|
import eu.kanade.domain.anime.interactor.NetworkToLocalAnime
|
||||||
import eu.kanade.domain.anime.interactor.UpdateAnime
|
import eu.kanade.domain.anime.interactor.UpdateAnime
|
||||||
import eu.kanade.domain.anime.model.Anime
|
import eu.kanade.domain.anime.model.Anime
|
||||||
import eu.kanade.domain.anime.model.toDomainAnime
|
|
||||||
import eu.kanade.domain.anime.model.toAnimeUpdate
|
import eu.kanade.domain.anime.model.toAnimeUpdate
|
||||||
|
import eu.kanade.domain.anime.model.toDomainAnime
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import eu.kanade.tachiyomi.animeextension.AnimeExtensionManager
|
import eu.kanade.tachiyomi.animeextension.AnimeExtensionManager
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
|
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
|
||||||
|
@ -42,7 +42,7 @@ abstract class AnimeSearchScreenModel<T>(
|
||||||
protected lateinit var extensionFilter: String
|
protected lateinit var extensionFilter: String
|
||||||
|
|
||||||
private val sources by lazy { getSelectedSources() }
|
private val sources by lazy { getSelectedSources() }
|
||||||
private val pinnedSources by lazy { sourcePreferences.pinnedSources().get() }
|
private val pinnedSources by lazy { sourcePreferences.pinnedAnimeSources().get() }
|
||||||
|
|
||||||
private val sortComparator = { map: Map<AnimeCatalogueSource, AnimeSearchItemResult> ->
|
private val sortComparator = { map: Map<AnimeCatalogueSource, AnimeSearchItemResult> ->
|
||||||
compareBy<AnimeCatalogueSource>(
|
compareBy<AnimeCatalogueSource>(
|
||||||
|
@ -95,8 +95,8 @@ abstract class AnimeSearchScreenModel<T>(
|
||||||
val enabledSources = getEnabledSources()
|
val enabledSources = getEnabledSources()
|
||||||
|
|
||||||
if (filter.isEmpty()) {
|
if (filter.isEmpty()) {
|
||||||
val shouldSearchPinnedOnly = sourcePreferences.searchPinnedSourcesOnly().get()
|
val shouldSearchPinnedOnly = sourcePreferences.searchPinnedAnimeSourcesOnly().get()
|
||||||
val pinnedSources = sourcePreferences.pinnedSources().get()
|
val pinnedSources = sourcePreferences.pinnedAnimeSources().get()
|
||||||
|
|
||||||
return enabledSources.filter {
|
return enabledSources.filter {
|
||||||
if (shouldSearchPinnedOnly) {
|
if (shouldSearchPinnedOnly) {
|
||||||
|
|
|
@ -9,8 +9,8 @@ import cafe.adriel.voyager.core.screen.uniqueScreenKey
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
import eu.kanade.presentation.animebrowse.GlobalAnimeSearchScreen
|
import eu.kanade.presentation.animebrowse.GlobalAnimeSearchScreen
|
||||||
import eu.kanade.tachiyomi.ui.browse.animesource.browse.BrowseAnimeSourceScreen
|
|
||||||
import eu.kanade.tachiyomi.ui.anime.AnimeScreen
|
import eu.kanade.tachiyomi.ui.anime.AnimeScreen
|
||||||
|
import eu.kanade.tachiyomi.ui.browse.animesource.browse.BrowseAnimeSourceScreen
|
||||||
|
|
||||||
class GlobalAnimeSearchScreen(
|
class GlobalAnimeSearchScreen(
|
||||||
val searchQuery: String = "",
|
val searchQuery: String = "",
|
||||||
|
|
|
@ -18,7 +18,7 @@ class GlobalAnimeSearchScreenModel(
|
||||||
) : AnimeSearchScreenModel<GlobalAnimeSearchState>(GlobalAnimeSearchState(searchQuery = initialQuery)) {
|
) : AnimeSearchScreenModel<GlobalAnimeSearchState>(GlobalAnimeSearchState(searchQuery = initialQuery)) {
|
||||||
|
|
||||||
val incognitoMode = preferences.incognitoMode()
|
val incognitoMode = preferences.incognitoMode()
|
||||||
val lastUsedSourceId = sourcePreferences.lastUsedSource()
|
val lastUsedSourceId = sourcePreferences.lastUsedAnimeSource()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
extensionFilter = initialExtensionFilter
|
extensionFilter = initialExtensionFilter
|
||||||
|
|
|
@ -12,8 +12,8 @@ import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
import eu.kanade.presentation.animebrowse.MigrateAnimeScreen
|
import eu.kanade.presentation.animebrowse.MigrateAnimeScreen
|
||||||
import eu.kanade.presentation.components.LoadingScreen
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateAnimeSearchScreen
|
|
||||||
import eu.kanade.tachiyomi.ui.anime.AnimeScreen
|
import eu.kanade.tachiyomi.ui.anime.AnimeScreen
|
||||||
|
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateAnimeSearchScreen
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ data class MigrationAnimeScreen(
|
||||||
navigateUp = navigator::pop,
|
navigateUp = navigator::pop,
|
||||||
title = state.source!!.name,
|
title = state.source!!.name,
|
||||||
state = state,
|
state = state,
|
||||||
onClickItem = { navigator.push(MigrateSearchScreen(it.id)) },
|
onClickItem = { navigator.push(MigrateAnimeSearchScreen(it.id)) },
|
||||||
onClickCover = { navigator.push(AnimeScreen(it.id)) },
|
onClickCover = { navigator.push(AnimeScreen(it.id)) },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@ package eu.kanade.tachiyomi.ui.browse.migration.animesources
|
||||||
import cafe.adriel.voyager.core.model.StateScreenModel
|
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||||
import cafe.adriel.voyager.core.model.coroutineScope
|
import cafe.adriel.voyager.core.model.coroutineScope
|
||||||
import eu.kanade.domain.animesource.interactor.GetAnimeSourcesWithFavoriteCount
|
import eu.kanade.domain.animesource.interactor.GetAnimeSourcesWithFavoriteCount
|
||||||
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
|
||||||
import eu.kanade.domain.animesource.model.AnimeSource
|
import eu.kanade.domain.animesource.model.AnimeSource
|
||||||
|
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.system.logcat
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
|
|
|
@ -30,9 +30,9 @@ import eu.kanade.presentation.components.SearchToolbar
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.animesource.LocalAnimeSource
|
import eu.kanade.tachiyomi.animesource.LocalAnimeSource
|
||||||
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||||
|
import eu.kanade.tachiyomi.ui.anime.AnimeScreen
|
||||||
import eu.kanade.tachiyomi.ui.browse.animesource.browse.BrowseAnimeSourceScreenModel
|
import eu.kanade.tachiyomi.ui.browse.animesource.browse.BrowseAnimeSourceScreenModel
|
||||||
import eu.kanade.tachiyomi.ui.home.HomeScreen
|
import eu.kanade.tachiyomi.ui.home.HomeScreen
|
||||||
import eu.kanade.tachiyomi.ui.anime.AnimeScreen
|
|
||||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||||
import eu.kanade.tachiyomi.util.Constants
|
import eu.kanade.tachiyomi.util.Constants
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
|
@ -27,30 +27,30 @@ import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
import cafe.adriel.voyager.core.screen.Screen
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
import eu.kanade.domain.category.interactor.GetCategories
|
|
||||||
import eu.kanade.domain.category.interactor.SetAnimeCategories
|
|
||||||
import eu.kanade.domain.episode.interactor.UpdateEpisode
|
|
||||||
import eu.kanade.domain.episode.model.toEpisodeUpdate
|
|
||||||
import eu.kanade.domain.anime.interactor.UpdateAnime
|
import eu.kanade.domain.anime.interactor.UpdateAnime
|
||||||
import eu.kanade.domain.anime.model.Anime
|
import eu.kanade.domain.anime.model.Anime
|
||||||
import eu.kanade.domain.anime.model.AnimeUpdate
|
import eu.kanade.domain.anime.model.AnimeUpdate
|
||||||
import eu.kanade.domain.anime.model.hasCustomCover
|
import eu.kanade.domain.anime.model.hasCustomCover
|
||||||
import eu.kanade.domain.animetrack.interactor.GetAnimeTracks
|
import eu.kanade.domain.animetrack.interactor.GetAnimeTracks
|
||||||
import eu.kanade.domain.animetrack.interactor.InsertAnimeTrack
|
import eu.kanade.domain.animetrack.interactor.InsertAnimeTrack
|
||||||
|
import eu.kanade.domain.category.interactor.GetCategories
|
||||||
|
import eu.kanade.domain.category.interactor.SetAnimeCategories
|
||||||
import eu.kanade.domain.episode.interactor.GetEpisodeByAnimeId
|
import eu.kanade.domain.episode.interactor.GetEpisodeByAnimeId
|
||||||
import eu.kanade.domain.episode.interactor.SyncEpisodesWithSource
|
import eu.kanade.domain.episode.interactor.SyncEpisodesWithSource
|
||||||
|
import eu.kanade.domain.episode.interactor.UpdateEpisode
|
||||||
|
import eu.kanade.domain.episode.model.toEpisodeUpdate
|
||||||
import eu.kanade.presentation.animebrowse.MigrateAnimeSearchScreen
|
import eu.kanade.presentation.animebrowse.MigrateAnimeSearchScreen
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.core.preference.Preference
|
|
||||||
import eu.kanade.tachiyomi.core.preference.PreferenceStore
|
|
||||||
import eu.kanade.tachiyomi.data.track.EnhancedAnimeTrackService
|
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeSource
|
import eu.kanade.tachiyomi.animesource.AnimeSource
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
||||||
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||||
|
import eu.kanade.tachiyomi.core.preference.Preference
|
||||||
|
import eu.kanade.tachiyomi.core.preference.PreferenceStore
|
||||||
import eu.kanade.tachiyomi.data.cache.AnimeCoverCache
|
import eu.kanade.tachiyomi.data.cache.AnimeCoverCache
|
||||||
import eu.kanade.tachiyomi.ui.browse.migration.AnimeMigrationFlags
|
import eu.kanade.tachiyomi.data.track.EnhancedAnimeTrackService
|
||||||
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
import eu.kanade.tachiyomi.ui.anime.AnimeScreen
|
import eu.kanade.tachiyomi.ui.anime.AnimeScreen
|
||||||
|
import eu.kanade.tachiyomi.ui.browse.migration.AnimeMigrationFlags
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
|
|
|
@ -2,9 +2,9 @@ package eu.kanade.tachiyomi.ui.browse.migration.search
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import cafe.adriel.voyager.core.model.coroutineScope
|
import cafe.adriel.voyager.core.model.coroutineScope
|
||||||
import eu.kanade.domain.base.BasePreferences
|
|
||||||
import eu.kanade.domain.anime.interactor.GetAnime
|
import eu.kanade.domain.anime.interactor.GetAnime
|
||||||
import eu.kanade.domain.anime.model.Anime
|
import eu.kanade.domain.anime.model.Anime
|
||||||
|
import eu.kanade.domain.base.BasePreferences
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
|
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
|
||||||
import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
||||||
|
@ -38,7 +38,7 @@ class MigrateAnimeSearchScreenModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
val incognitoMode = preferences.incognitoMode()
|
val incognitoMode = preferences.incognitoMode()
|
||||||
val lastUsedSourceId = sourcePreferences.lastUsedSource()
|
val lastUsedSourceId = sourcePreferences.lastUsedAnimeSource()
|
||||||
|
|
||||||
override fun getEnabledSources(): List<AnimeCatalogueSource> {
|
override fun getEnabledSources(): List<AnimeCatalogueSource> {
|
||||||
val enabledLanguages = sourcePreferences.enabledLanguages().get()
|
val enabledLanguages = sourcePreferences.enabledLanguages().get()
|
||||||
|
|
|
@ -25,7 +25,7 @@ fun Screen.migrateSourceTab(): TabContent {
|
||||||
val state by screenModel.state.collectAsState()
|
val state by screenModel.state.collectAsState()
|
||||||
|
|
||||||
return TabContent(
|
return TabContent(
|
||||||
titleRes = R.string.label_migration_manga,
|
titleRes = R.string.label_migration,
|
||||||
actions = listOf(
|
actions = listOf(
|
||||||
AppBar.Action(
|
AppBar.Action(
|
||||||
title = stringResource(R.string.migration_help_guide),
|
title = stringResource(R.string.migration_help_guide),
|
||||||
|
|
|
@ -51,7 +51,7 @@ import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.source.LocalSource
|
import eu.kanade.tachiyomi.source.LocalSource
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing
|
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing
|
||||||
import eu.kanade.tachiyomi.ui.category.CategoryScreen
|
import eu.kanade.tachiyomi.ui.category.CategoriesTab
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
||||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||||
import eu.kanade.tachiyomi.util.Constants
|
import eu.kanade.tachiyomi.util.Constants
|
||||||
|
@ -242,7 +242,7 @@ data class BrowseSourceScreen(
|
||||||
ChangeCategoryDialog(
|
ChangeCategoryDialog(
|
||||||
initialSelection = dialog.initialSelection,
|
initialSelection = dialog.initialSelection,
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
onEditCategories = { navigator.push(CategoryScreen()) },
|
onEditCategories = { navigator.push(CategoriesTab(true)) },
|
||||||
onConfirm = { include, _ ->
|
onConfirm = { include, _ ->
|
||||||
screenModel.changeMangaFavorite(dialog.manga)
|
screenModel.changeMangaFavorite(dialog.manga)
|
||||||
screenModel.moveMangaToCategories(dialog.manga, include)
|
screenModel.moveMangaToCategories(dialog.manga, include)
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue