mirror of
https://github.com/aniyomiorg/aniyomi.git
synced 2024-11-22 12:48:15 +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>>> {
|
||||
return combine(
|
||||
preferences.enabledLanguages().changes(),
|
||||
preferences.disabledSources().changes(),
|
||||
preferences.disabledAnimeSources().changes(),
|
||||
repository.getOnlineSources(),
|
||||
) { enabledLanguage, disabledSource, onlineSources ->
|
||||
val sortedSources = onlineSources.sortedWith(
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package eu.kanade.domain.episode.model
|
||||
|
||||
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.manga.model.TriStateFilter
|
||||
import eu.kanade.tachiyomi.data.animedownload.AnimeDownloadManager
|
||||
import eu.kanade.tachiyomi.data.animedownload.model.AnimeDownload
|
||||
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 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
|
||||
|
||||
// region Badges
|
||||
|
|
|
@ -42,5 +42,5 @@ class SourcePreferences(
|
|||
|
||||
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.WorkManager
|
||||
import androidx.work.WorkerParameters
|
||||
import eu.kanade.domain.anime.interactor.GetAnime
|
||||
import eu.kanade.domain.animetrack.interactor.GetAnimeTracks
|
||||
import eu.kanade.domain.animetrack.interactor.InsertAnimeTrack
|
||||
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.ui.anime.AnimeScreenState
|
||||
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.api.get
|
||||
|
||||
|
@ -252,6 +252,7 @@ private fun AnimeScreenSmallImpl(
|
|||
onClickDownload = onDownloadActionClicked,
|
||||
onClickEditCategory = onEditCategoryClicked,
|
||||
onClickMigrate = onMigrateClicked,
|
||||
changeAnimeSkipIntro = changeAnimeSkipIntro,
|
||||
actionModeCounter = episodes.count { it.selected },
|
||||
onSelectAll = { onAllEpisodeSelected(true) },
|
||||
onInvertSelection = { onInvertSelection() },
|
||||
|
|
|
@ -2,7 +2,6 @@ package eu.kanade.presentation.anime
|
|||
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
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.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
|
@ -39,11 +30,9 @@ import androidx.compose.ui.draw.alpha
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.components.Divider
|
||||
import eu.kanade.presentation.components.DropdownMenu
|
||||
import eu.kanade.presentation.components.TrackLogoIcon
|
||||
import eu.kanade.presentation.components.VerticalDivider
|
||||
import eu.kanade.presentation.manga.TrackDetailsItem
|
||||
|
|
|
@ -5,10 +5,7 @@ import androidx.compose.animation.fadeIn
|
|||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInVertically
|
||||
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.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
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.more.settings.widget.SwitchPreferenceWidget
|
||||
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
|
||||
|
||||
@Composable
|
||||
fun AnimeExtensionFilterScreen(
|
||||
navigateUp: () -> Unit,
|
||||
state: AnimeExtensionFilterState.Success,
|
||||
state: ExtensionFilterState.Success,
|
||||
onClickToggle: (String) -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
|
@ -31,13 +31,13 @@ fun AnimeExtensionFilterScreen(
|
|||
)
|
||||
},
|
||||
) { contentPadding ->
|
||||
if (state.isEmpty) {
|
||||
EmptyScreen(
|
||||
textResource = R.string.empty_screen,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
return@Scaffold
|
||||
}
|
||||
if (state.isEmpty) {
|
||||
EmptyScreen(
|
||||
textResource = R.string.empty_screen,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
return@Scaffold
|
||||
}
|
||||
AnimeExtensionFilterContent(
|
||||
contentPadding = contentPadding,
|
||||
state = state,
|
||||
|
@ -49,7 +49,7 @@ fun AnimeExtensionFilterScreen(
|
|||
@Composable
|
||||
private fun AnimeExtensionFilterContent(
|
||||
contentPadding: PaddingValues,
|
||||
state: AnimeExtensionFilterState.Success,
|
||||
state: ExtensionFilterState.Success,
|
||||
onClickLang: (String) -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
|
|
@ -83,7 +83,7 @@ private fun AnimeSourcesFilterContent(
|
|||
AnimeSourcesFilterItem(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
source = source,
|
||||
enabled = "${source.id}" !in state.disabledSources,
|
||||
isEnabled = "${source.id}" !in state.disabledSources,
|
||||
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.browse.BrowseAnimeSourceScreenModel.Listing
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
||||
@Composable
|
||||
fun AnimeSourcesScreen(
|
||||
|
|
|
@ -19,8 +19,8 @@ import eu.kanade.presentation.components.Scaffold
|
|||
import eu.kanade.presentation.util.padding
|
||||
import eu.kanade.tachiyomi.R
|
||||
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.GlobalAnimeSearchState
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
|
||||
@Composable
|
||||
|
|
|
@ -10,7 +10,6 @@ import eu.kanade.presentation.anime.components.BaseAnimeListItem
|
|||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.components.FastScrollLazyColumn
|
||||
import eu.kanade.presentation.components.LoadingScreen
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.tachiyomi.R
|
||||
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.Scaffold
|
||||
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.migration.search.MigrateAnimeSearchState
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
|
||||
@Composable
|
||||
|
|
|
@ -39,12 +39,12 @@ import eu.kanade.presentation.util.plus
|
|||
import eu.kanade.presentation.util.secondaryItemAlpha
|
||||
import eu.kanade.presentation.util.topSmallPaddingValues
|
||||
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
|
||||
|
||||
@Composable
|
||||
fun MigrateAnimeSourceScreen(
|
||||
state: MigrateAnimeSourcesState,
|
||||
state: MigrateAnimeSourceState,
|
||||
contentPadding: PaddingValues,
|
||||
onClickItem: (AnimeSource) -> Unit,
|
||||
onToggleSortingDirection: () -> Unit,
|
||||
|
|
|
@ -13,8 +13,8 @@ import androidx.paging.LoadState
|
|||
import androidx.paging.compose.LazyPagingItems
|
||||
import eu.kanade.domain.anime.model.Anime
|
||||
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.components.BrowseSourceLoadingItem
|
||||
import eu.kanade.presentation.components.CommonMangaItemDefaults
|
||||
import eu.kanade.presentation.components.MangaComfortableGridItem
|
||||
import eu.kanade.presentation.util.plus
|
||||
|
|
|
@ -13,8 +13,8 @@ import androidx.paging.LoadState
|
|||
import androidx.paging.compose.LazyPagingItems
|
||||
import eu.kanade.domain.anime.model.Anime
|
||||
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.components.BrowseSourceLoadingItem
|
||||
import eu.kanade.presentation.components.CommonMangaItemDefaults
|
||||
import eu.kanade.presentation.components.MangaCompactGridItem
|
||||
import eu.kanade.presentation.util.plus
|
||||
|
|
|
@ -10,8 +10,8 @@ import androidx.paging.compose.LazyPagingItems
|
|||
import androidx.paging.compose.items
|
||||
import eu.kanade.domain.anime.model.Anime
|
||||
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.components.BrowseSourceLoadingItem
|
||||
import eu.kanade.presentation.components.CommonMangaItemDefaults
|
||||
import eu.kanade.presentation.components.LazyColumn
|
||||
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.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.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import eu.kanade.domain.animehistory.model.AnimeHistoryWithRelations
|
||||
import eu.kanade.presentation.animehistory.components.AnimeHistoryContent
|
||||
import eu.kanade.presentation.components.AppBarTitle
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.components.LoadingScreen
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.components.SearchToolbar
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.animehistory.AnimeHistoryScreenModel
|
||||
import eu.kanade.tachiyomi.ui.animehistory.AnimeHistoryState
|
||||
import eu.kanade.tachiyomi.ui.history.anime.AnimeHistoryScreenModel
|
||||
import eu.kanade.tachiyomi.ui.history.anime.AnimeHistoryState
|
||||
import java.util.Date
|
||||
|
||||
@Composable
|
||||
fun AnimeHistoryScreen(
|
||||
state: AnimeHistoryState,
|
||||
contentPadding: PaddingValues,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
onSearchQueryChange: (String?) -> Unit,
|
||||
onClickCover: (animeId: Long) -> Unit,
|
||||
onClickResume: (animeId: Long, episodeId: Long) -> Unit,
|
||||
onDialogChange: (AnimeHistoryScreenModel.Dialog?) -> Unit,
|
||||
) {
|
||||
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) },
|
||||
) { contentPadding ->
|
||||
) { _ ->
|
||||
state.list.let {
|
||||
if (it == null) {
|
||||
LoadingScreen(modifier = Modifier.padding(contentPadding))
|
||||
|
|
|
@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.calculateStartPadding
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
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.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
package eu.kanade.presentation.animelib.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
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.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
@ -15,16 +10,13 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.core.prefs.PreferenceMutableState
|
||||
import eu.kanade.domain.animelib.model.AnimelibAnime
|
||||
import eu.kanade.domain.library.model.LibraryDisplayMode
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.components.HorizontalPager
|
||||
import eu.kanade.presentation.components.PagerState
|
||||
import eu.kanade.presentation.library.components.LibraryPagerEmptyScreen
|
||||
import eu.kanade.presentation.util.plus
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.animelib.AnimelibItem
|
||||
|
||||
@Composable
|
||||
|
|
|
@ -1,18 +1,11 @@
|
|||
package eu.kanade.presentation.animeupdates
|
||||
|
||||
import android.content.Context
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
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.SnackbarHostState
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
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.PullRefresh
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.updates.updatesLastUpdatedItem
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.animedownload.model.AnimeDownload
|
||||
import eu.kanade.tachiyomi.ui.animeupdates.AnimeUpdatesItem
|
||||
import eu.kanade.tachiyomi.ui.animeupdates.AnimeUpdatesState
|
||||
import eu.kanade.tachiyomi.ui.player.setting.PlayerPreferences
|
||||
import eu.kanade.tachiyomi.ui.player.settings.PlayerPreferences
|
||||
import eu.kanade.tachiyomi.ui.updates.anime.AnimeUpdatesItem
|
||||
import eu.kanade.tachiyomi.ui.updates.anime.AnimeUpdatesState
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import uy.kohesive.injekt.Injekt
|
||||
|
@ -46,6 +38,7 @@ import kotlin.time.Duration.Companion.seconds
|
|||
fun AnimeUpdateScreen(
|
||||
state: AnimeUpdatesState,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
contentPadding: PaddingValues,
|
||||
lastUpdated: Long,
|
||||
relativeTime: Int,
|
||||
onClickCover: (AnimeUpdatesItem) -> Unit,
|
||||
|
@ -54,10 +47,10 @@ fun AnimeUpdateScreen(
|
|||
onUpdateLibrary: () -> Boolean,
|
||||
onDownloadEpisode: (List<AnimeUpdatesItem>, EpisodeDownloadAction) -> Unit,
|
||||
onMultiBookmarkClicked: (List<AnimeUpdatesItem>, bookmark: Boolean) -> Unit,
|
||||
onMultiMarkAsReadClicked: (List<AnimeUpdatesItem>, read: Boolean) -> Unit,
|
||||
onMultiMarkAsSeenClicked: (List<AnimeUpdatesItem>, seen: Boolean) -> Unit,
|
||||
onMultiDeleteClicked: (List<AnimeUpdatesItem>) -> 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) })
|
||||
|
||||
|
@ -69,13 +62,13 @@ fun AnimeUpdateScreen(
|
|||
selected = state.selected,
|
||||
onDownloadEpisode = onDownloadEpisode,
|
||||
onMultiBookmarkClicked = onMultiBookmarkClicked,
|
||||
onMultiMarkAsSeenClicked = onMultiMarkAsReadClicked,
|
||||
onMultiMarkAsSeenClicked = onMultiMarkAsSeenClicked,
|
||||
onMultiDeleteClicked = onMultiDeleteClicked,
|
||||
onOpenEpisode = onOpenEpisode,
|
||||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||
) { contentPadding ->
|
||||
) {
|
||||
when {
|
||||
state.isLoading -> LoadingScreen(modifier = Modifier.padding(contentPadding))
|
||||
state.items.isEmpty() -> EmptyScreen(
|
||||
|
@ -105,14 +98,14 @@ fun AnimeUpdateScreen(
|
|||
contentPadding = contentPadding,
|
||||
) {
|
||||
if (lastUpdated > 0L) {
|
||||
updatesLastUpdatedItem(lastUpdated)
|
||||
animeupdatesLastUpdatedItem(lastUpdated)
|
||||
}
|
||||
animeupdatesUiItems(
|
||||
uiModels = state.getUiModel(context, relativeTime),
|
||||
selectionMode = state.selectionMode,
|
||||
onUpdateSelected = onUpdateSelected,
|
||||
onClickCover = onClickCover,
|
||||
onClickUpdate = { onOpenEpisode },
|
||||
onClickUpdate = onOpenEpisode,
|
||||
onDownloadEpisode = onDownloadEpisode,
|
||||
)
|
||||
}
|
||||
|
@ -129,10 +122,9 @@ private fun AnimeUpdatesBottomBar(
|
|||
onMultiBookmarkClicked: (List<AnimeUpdatesItem>, bookmark: Boolean) -> Unit,
|
||||
onMultiMarkAsSeenClicked: (List<AnimeUpdatesItem>, seen: Boolean) -> Unit,
|
||||
onMultiDeleteClicked: (List<AnimeUpdatesItem>) -> Unit,
|
||||
onOpenEpisode: (List<AnimeUpdatesItem>, context: Context, altPlayer: Boolean) -> Unit,
|
||||
onOpenEpisode: (AnimeUpdatesItem, altPlayer: Boolean) -> Unit,
|
||||
) {
|
||||
val playerPreferences: PlayerPreferences = Injekt.get()
|
||||
val context = LocalContext.current
|
||||
AnimeBottomActionMenu(
|
||||
visible = selected.isNotEmpty(),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
@ -157,10 +149,10 @@ private fun AnimeUpdatesBottomBar(
|
|||
onMultiDeleteClicked(selected)
|
||||
}.takeIf { selected.fastAny { it.downloadStateProvider() == AnimeDownload.State.DOWNLOADED } },
|
||||
onExternalClicked = {
|
||||
onOpenEpisode(selected, context, true)
|
||||
onOpenEpisode(selected[0], true)
|
||||
}.takeIf { !playerPreferences.alwaysUseExternalPlayer().get() && selected.size == 1 },
|
||||
onInternalClicked = {
|
||||
onOpenEpisode(selected, context,false)
|
||||
onOpenEpisode(selected[0], true)
|
||||
}.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.tachiyomi.R
|
||||
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 kotlin.time.Duration.Companion.minutes
|
||||
|
||||
|
@ -82,7 +82,7 @@ fun LazyListScope.animeupdatesUiItems(
|
|||
selectionMode: Boolean,
|
||||
onUpdateSelected: (AnimeUpdatesItem, Boolean, Boolean, Boolean) -> Unit,
|
||||
onClickCover: (AnimeUpdatesItem) -> Unit,
|
||||
onClickUpdate: (AnimeUpdatesItem) -> Unit,
|
||||
onClickUpdate: (AnimeUpdatesItem, altPlayer: Boolean) -> Unit,
|
||||
onDownloadEpisode: (List<AnimeUpdatesItem>, EpisodeDownloadAction) -> Unit,
|
||||
) {
|
||||
items(
|
||||
|
@ -119,7 +119,7 @@ fun LazyListScope.animeupdatesUiItems(
|
|||
onClick = {
|
||||
when {
|
||||
selectionMode -> onUpdateSelected(updatesItem, !updatesItem.selected, true, false)
|
||||
else -> onClickUpdate(updatesItem)
|
||||
else -> onClickUpdate(updatesItem, false)
|
||||
}
|
||||
},
|
||||
onClickCover = { onClickCover(updatesItem) }.takeIf { !selectionMode },
|
||||
|
|
|
@ -6,49 +6,40 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
|||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import eu.kanade.domain.category.model.Category
|
||||
import eu.kanade.presentation.category.components.CategoryContent
|
||||
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.util.padding
|
||||
import eu.kanade.presentation.util.plus
|
||||
import eu.kanade.presentation.util.topSmallPaddingValues
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.animecategory.AnimeCategoryScreenState
|
||||
import eu.kanade.tachiyomi.ui.category.anime.AnimeCategoryScreenState
|
||||
|
||||
@Composable
|
||||
fun AnimeCategoryScreen(
|
||||
state: AnimeCategoryScreenState.Success,
|
||||
contentPadding: PaddingValues,
|
||||
onClickCreate: () -> Unit,
|
||||
onClickRename: (Category) -> Unit,
|
||||
onClickDelete: (Category) -> Unit,
|
||||
onClickMoveUp: (Category) -> Unit,
|
||||
onClickMoveDown: (Category) -> Unit,
|
||||
navigateUp: () -> Unit,
|
||||
) {
|
||||
val lazyListState = rememberLazyListState()
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
AppBar(
|
||||
title = stringResource(R.string.action_edit_categories),
|
||||
navigateUp = navigateUp,
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
CategoryFloatingActionButton(
|
||||
lazyListState = lazyListState,
|
||||
onCreate = onClickCreate,
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
) {
|
||||
if (state.isEmpty) {
|
||||
EmptyScreen(
|
||||
textResource = R.string.information_empty_category,
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
return@Scaffold
|
||||
}
|
||||
|
@ -56,7 +47,7 @@ fun AnimeCategoryScreen(
|
|||
CategoryContent(
|
||||
categories = state.categories,
|
||||
lazyListState = lazyListState,
|
||||
paddingValues = paddingValues + topSmallPaddingValues + PaddingValues(horizontal = MaterialTheme.padding.medium),
|
||||
paddingValues = contentPadding + topSmallPaddingValues + PaddingValues(horizontal = MaterialTheme.padding.medium),
|
||||
onClickRename = onClickRename,
|
||||
onClickDelete = onClickDelete,
|
||||
onMoveUp = onClickMoveUp,
|
||||
|
|
|
@ -6,49 +6,40 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
|||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import eu.kanade.domain.category.model.Category
|
||||
import eu.kanade.presentation.category.components.CategoryContent
|
||||
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.util.padding
|
||||
import eu.kanade.presentation.util.plus
|
||||
import eu.kanade.presentation.util.topSmallPaddingValues
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryScreenState
|
||||
import eu.kanade.tachiyomi.ui.category.manga.CategoryScreenState
|
||||
|
||||
@Composable
|
||||
fun CategoryScreen(
|
||||
state: CategoryScreenState.Success,
|
||||
contentPadding: PaddingValues,
|
||||
onClickCreate: () -> Unit,
|
||||
onClickRename: (Category) -> Unit,
|
||||
onClickDelete: (Category) -> Unit,
|
||||
onClickMoveUp: (Category) -> Unit,
|
||||
onClickMoveDown: (Category) -> Unit,
|
||||
navigateUp: () -> Unit,
|
||||
) {
|
||||
val lazyListState = rememberLazyListState()
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
AppBar(
|
||||
title = stringResource(R.string.action_edit_categories),
|
||||
navigateUp = navigateUp,
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
CategoryFloatingActionButton(
|
||||
lazyListState = lazyListState,
|
||||
onCreate = onClickCreate,
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
) {
|
||||
if (state.isEmpty) {
|
||||
EmptyScreen(
|
||||
textResource = R.string.information_empty_category,
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
return@Scaffold
|
||||
}
|
||||
|
@ -56,7 +47,7 @@ fun CategoryScreen(
|
|||
CategoryContent(
|
||||
categories = state.categories,
|
||||
lazyListState = lazyListState,
|
||||
paddingValues = paddingValues + topSmallPaddingValues + PaddingValues(horizontal = MaterialTheme.padding.medium),
|
||||
paddingValues = contentPadding + topSmallPaddingValues + PaddingValues(horizontal = MaterialTheme.padding.medium),
|
||||
onClickRename = onClickRename,
|
||||
onClickDelete = onClickDelete,
|
||||
onMoveUp = onClickMoveUp,
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package eu.kanade.presentation.components
|
||||
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
|
@ -30,6 +33,7 @@ import androidx.compose.runtime.key
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
|
@ -149,22 +153,41 @@ fun AppBar(
|
|||
fun AppBarTitle(
|
||||
title: String?,
|
||||
subtitle: String? = null,
|
||||
count: Int = 0,
|
||||
) {
|
||||
Column {
|
||||
title?.let {
|
||||
if (count > 0) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = it,
|
||||
text = title!!,
|
||||
maxLines = 1,
|
||||
modifier = Modifier.weight(1f, false),
|
||||
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 {
|
||||
Text(
|
||||
text = it,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
} else {
|
||||
Column {
|
||||
title?.let {
|
||||
Text(
|
||||
text = it,
|
||||
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() }
|
||||
},
|
||||
isActionMode = false,
|
||||
downloadedOnlyMode = downloadedOnlyMode,
|
||||
incognitoMode = incognitoMode,
|
||||
scrollBehavior = scrollBehavior,
|
||||
onCancelActionMode = cancelAction,
|
||||
)
|
||||
|
|
|
@ -32,15 +32,12 @@ fun TabbedScreen(
|
|||
startIndex: Int? = null,
|
||||
searchQuery: String? = null,
|
||||
onChangeSearchQuery: (String?) -> Unit = {},
|
||||
incognitoMode: Boolean = false,
|
||||
downloadedOnlyMode: Boolean = false,
|
||||
state: PagerState = rememberPagerState(),
|
||||
scrollable: Boolean = false,
|
||||
searchQueryAnime: String? = null,
|
||||
onChangeSearchQueryAnime: (String?) -> Unit = {},
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val state = rememberPagerState()
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
||||
LaunchedEffect(startIndex) {
|
||||
|
@ -65,9 +62,8 @@ fun TabbedScreen(
|
|||
else -> onChangeSearchQueryAnime
|
||||
}
|
||||
|
||||
val appBarTitleText = if (tab.numberTitle == 0) stringResource(titleRes) else tab.numberTitle.toString()
|
||||
SearchToolbar(
|
||||
titleContent = { AppBarTitle(appBarTitleText) },
|
||||
titleContent = { AppBarTitle(stringResource(titleRes), null, tab.numberTitle) },
|
||||
searchEnabled = searchEnabled,
|
||||
searchQuery = if (searchEnabled) actualQuery else null,
|
||||
onChangeSearchQuery = actualOnChange,
|
||||
|
|
|
@ -2,55 +2,32 @@ package eu.kanade.presentation.history
|
|||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
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.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import eu.kanade.domain.history.model.HistoryWithRelations
|
||||
import eu.kanade.presentation.components.AppBarTitle
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.components.LoadingScreen
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.components.SearchToolbar
|
||||
import eu.kanade.presentation.history.components.HistoryContent
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
|
||||
import eu.kanade.tachiyomi.ui.history.HistoryState
|
||||
import eu.kanade.tachiyomi.ui.history.manga.HistoryState
|
||||
import eu.kanade.tachiyomi.ui.history.manga.MangaHistoryScreenModel
|
||||
import java.util.Date
|
||||
|
||||
@Composable
|
||||
fun HistoryScreen(
|
||||
state: HistoryState,
|
||||
contentPadding: PaddingValues,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
onSearchQueryChange: (String?) -> Unit,
|
||||
onClickCover: (mangaId: Long) -> Unit,
|
||||
onClickResume: (mangaId: Long, chapterId: Long) -> Unit,
|
||||
onDialogChange: (HistoryScreenModel.Dialog?) -> Unit,
|
||||
onDialogChange: (MangaHistoryScreenModel.Dialog?) -> Unit,
|
||||
) {
|
||||
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) },
|
||||
) { contentPadding ->
|
||||
) { _ ->
|
||||
state.list.let {
|
||||
if (it == null) {
|
||||
LoadingScreen(modifier = Modifier.padding(contentPadding))
|
||||
|
@ -70,7 +47,7 @@ fun HistoryScreen(
|
|||
contentPadding = contentPadding,
|
||||
onClickCover = { history -> onClickCover(history.mangaId) },
|
||||
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
|
||||
.clickable(onClick = onClickResume)
|
||||
.height(HISTORY_ITEM_HEIGHT)
|
||||
.padding(horizontal = horizontalPadding, vertical = 8.dp),
|
||||
.padding(horizontal = MaterialTheme.padding.medium, vertical = MaterialTheme.padding.small),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
MangaCover.Book(
|
||||
|
@ -113,7 +113,7 @@ fun AnimeHistoryItem(
|
|||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = horizontalPadding, end = 8.dp),
|
||||
.padding(start = MaterialTheme.padding.medium, end = MaterialTheme.padding.small),
|
||||
) {
|
||||
val textStyle = MaterialTheme.typography.bodyMedium
|
||||
Text(
|
||||
|
|
|
@ -98,7 +98,7 @@ fun TrackChapterSelector(
|
|||
onDismissRequest: () -> Unit,
|
||||
isAnime: Boolean,
|
||||
) {
|
||||
val titleText = when(isAnime) {
|
||||
val titleText = when (isAnime) {
|
||||
true -> R.string.chapters
|
||||
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.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import eu.kanade.domain.library.service.LibraryPreferences
|
||||
import eu.kanade.presentation.components.Divider
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
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.TextPreferenceWidget
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.more.AnimeDownloadQueueState
|
||||
import eu.kanade.tachiyomi.ui.more.DownloadQueueState
|
||||
import eu.kanade.tachiyomi.util.Constants
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
@Composable
|
||||
fun MoreScreen(
|
||||
|
@ -45,9 +46,8 @@ fun MoreScreen(
|
|||
incognitoMode: Boolean,
|
||||
onIncognitoModeChange: (Boolean) -> Unit,
|
||||
isFDroid: Boolean,
|
||||
onClickHistory: () -> Unit,
|
||||
onClickAlt: () -> Unit,
|
||||
onClickDownloadQueue: () -> Unit,
|
||||
onClickAnimeCategories: () -> Unit,
|
||||
onClickCategories: () -> Unit,
|
||||
onClickStats: () -> Unit,
|
||||
onClickBackupAndRestore: () -> Unit,
|
||||
|
@ -101,22 +101,24 @@ fun MoreScreen(
|
|||
|
||||
item { Divider() }
|
||||
|
||||
val libraryPreferences: LibraryPreferences by injectLazy()
|
||||
|
||||
item {
|
||||
val bottomNavStyle = libraryPreferences.bottomNavStyle().get()
|
||||
val titleRes = when (bottomNavStyle) {
|
||||
0 -> R.string.label_recent_manga
|
||||
1 -> R.string.label_recent_updates
|
||||
2 -> R.string.label_manga
|
||||
else -> R.string.label_recent_manga
|
||||
else -> R.string.label_manga
|
||||
}
|
||||
val icon = when (bottomNavStyle) {
|
||||
0 -> Icons.Outlined.History
|
||||
1 -> ImageVector.vectorResource(id = R.drawable.ic_updates_outline_24dp)
|
||||
2 -> Icons.Outlined.CollectionsBookmark
|
||||
else -> Icons.Outlined.History
|
||||
else -> Icons.Outlined.CollectionsBookmark
|
||||
}
|
||||
TextPreferenceWidget(
|
||||
title = stringResource(titleRes),
|
||||
icon = icon,
|
||||
onPreferenceClick = onClickHistory,
|
||||
onPreferenceClick = onClickAlt,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -151,7 +153,7 @@ fun MoreScreen(
|
|||
}
|
||||
item {
|
||||
TextPreferenceWidget(
|
||||
title = stringResource(R.string.categories),
|
||||
title = stringResource(R.string.general_categories),
|
||||
icon = Icons.Outlined.Label,
|
||||
onPreferenceClick = onClickCategories,
|
||||
)
|
||||
|
|
|
@ -199,8 +199,10 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||
Preference.PreferenceItem.TextPreference(
|
||||
title = stringResource(R.string.pref_invalidate_download_cache),
|
||||
subtitle = stringResource(R.string.pref_invalidate_download_cache_summary),
|
||||
onClick = { Injekt.get<DownloadCache>().invalidateCache()
|
||||
Injekt.get<AnimeDownloadCache>().invalidateCache() },
|
||||
onClick = {
|
||||
Injekt.get<DownloadCache>().invalidateCache()
|
||||
Injekt.get<AnimeDownloadCache>().invalidateCache()
|
||||
},
|
||||
),
|
||||
Preference.PreferenceItem.TextPreference(
|
||||
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(
|
||||
title = stringResource(R.string.pref_category_nsfw_content),
|
||||
preferenceItems = listOf(
|
||||
|
|
|
@ -103,6 +103,7 @@ object SettingsDownloadScreen : SearchableSettings {
|
|||
}
|
||||
|
||||
val defaultDirPair = rememberDefaultDownloadDir()
|
||||
val externalDownloaderDirPair = rememberExternalDownloaderDownloadDir()
|
||||
val customDirEntryKey = currentDir.takeIf { it != defaultDirPair.first } ?: "custom"
|
||||
|
||||
return Preference.PreferenceItem.ListPreference(
|
||||
|
@ -115,6 +116,7 @@ object SettingsDownloadScreen : SearchableSettings {
|
|||
},
|
||||
entries = mapOf(
|
||||
defaultDirPair,
|
||||
externalDownloaderDirPair,
|
||||
customDirEntryKey to stringResource(R.string.custom_dir),
|
||||
),
|
||||
onValueChanged = {
|
||||
|
@ -129,6 +131,20 @@ object SettingsDownloadScreen : SearchableSettings {
|
|||
|
||||
@Composable
|
||||
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)
|
||||
return remember {
|
||||
val file = UniFile.fromFile(
|
||||
|
|
|
@ -35,8 +35,8 @@ import androidx.core.content.ContextCompat
|
|||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.Navigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.domain.category.interactor.GetAnimeCategories
|
||||
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.ResetCategoryFlags
|
||||
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_READ
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.ui.animecategory.AnimeCategoryScreen
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryScreen
|
||||
import eu.kanade.tachiyomi.ui.category.CategoriesTab
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import uy.kohesive.injekt.Injekt
|
||||
|
@ -155,7 +154,7 @@ object SettingsLibraryScreen : SearchableSettings {
|
|||
count = userAnimeCategoriesCount,
|
||||
userAnimeCategoriesCount,
|
||||
),
|
||||
onClick = { navigator.push(AnimeCategoryScreen()) },
|
||||
onClick = { navigator.push(CategoriesTab(false)) },
|
||||
),
|
||||
Preference.PreferenceItem.ListPreference(
|
||||
pref = libraryPreferences.defaultAnimeCategory(),
|
||||
|
@ -170,7 +169,7 @@ object SettingsLibraryScreen : SearchableSettings {
|
|||
count = userCategoriesCount,
|
||||
userCategoriesCount,
|
||||
),
|
||||
onClick = { navigator.push(CategoryScreen()) },
|
||||
onClick = { navigator.push(CategoriesTab(true)) },
|
||||
),
|
||||
Preference.PreferenceItem.ListPreference(
|
||||
pref = libraryPreferences.defaultCategory(),
|
||||
|
|
|
@ -6,10 +6,7 @@ import android.os.Build
|
|||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
|
@ -23,14 +20,13 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
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.presentation.more.settings.Preference
|
||||
import eu.kanade.presentation.util.collectAsState
|
||||
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 uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
@ -124,7 +120,7 @@ object SettingsPlayerScreen : SearchableSettings {
|
|||
@Composable
|
||||
private fun getInternalPlayerGroup(playerPreferences: PlayerPreferences, basePreferences: BasePreferences): Preference.PreferenceGroup {
|
||||
val scope = rememberCoroutineScope()
|
||||
val defaultSkipIntroLength by playerPreferences.skipLengthPreference().stateIn(scope).collectAsState()
|
||||
val defaultSkipIntroLength by playerPreferences.defaultIntroLength().stateIn(scope).collectAsState()
|
||||
val skipLengthPreference = playerPreferences.skipLengthPreference()
|
||||
val playerSmoothSeek = playerPreferences.playerSmoothSeek()
|
||||
val playerFullscreen = playerPreferences.playerFullscreen()
|
||||
|
@ -142,7 +138,7 @@ object SettingsPlayerScreen : SearchableSettings {
|
|||
initialSkipIntroLength = defaultSkipIntroLength,
|
||||
onDismissRequest = { showDialog = false },
|
||||
onValueChanged = { skipIntroLength ->
|
||||
playerPreferences.skipLengthPreference().set(skipIntroLength)
|
||||
playerPreferences.defaultIntroLength().set(skipIntroLength)
|
||||
showDialog = false
|
||||
},
|
||||
)
|
||||
|
@ -290,15 +286,14 @@ object SettingsPlayerScreen : SearchableSettings {
|
|||
)
|
||||
}
|
||||
|
||||
// TODO: chnage to new wheel Picker
|
||||
@Composable
|
||||
private fun SkipIntroLengthDialog(
|
||||
initialSkipIntroLength: Int,
|
||||
onDismissRequest: () -> Unit,
|
||||
onValueChanged: (skipIntroLength: Int) -> Unit,
|
||||
) {
|
||||
var skipIntroLengthValue by rememberSaveable { mutableStateOf(initialSkipIntroLength) }
|
||||
|
||||
val skipIntroLengthValue by rememberSaveable { mutableStateOf(initialSkipIntroLength) }
|
||||
var newLength = 0
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
title = { Text(text = stringResource(R.string.pref_intro_length)) },
|
||||
|
@ -308,16 +303,13 @@ object SettingsPlayerScreen : SearchableSettings {
|
|||
modifier = Modifier.weight(1f),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
NumberPicker(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clipToBounds(),
|
||||
value = skipIntroLengthValue,
|
||||
onValueChange = { skipIntroLengthValue = it },
|
||||
range = 1..255,
|
||||
label = { it.toString() },
|
||||
dividersColor = MaterialTheme.colorScheme.primary,
|
||||
textStyle = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.onSurface),
|
||||
WheelTextPicker(
|
||||
texts = remember { 1..255 }.map { "$it" },
|
||||
onScrollFinished = {
|
||||
newLength = it
|
||||
null
|
||||
},
|
||||
startIndex = skipIntroLengthValue,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -328,7 +320,7 @@ object SettingsPlayerScreen : SearchableSettings {
|
|||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = { onValueChanged(skipIntroLengthValue) }) {
|
||||
TextButton(onClick = { onValueChanged(newLength) }) {
|
||||
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
|
||||
|
||||
@Composable
|
||||
fun StatsScreenContent(
|
||||
state: StatsScreenState.Success,
|
||||
fun MangaStatsScreenContent(
|
||||
state: StatsScreenState.SuccessManga,
|
||||
paddingValues: PaddingValues,
|
||||
) {
|
||||
val statListState = rememberLazyListState()
|
||||
|
@ -53,7 +53,7 @@ fun StatsScreenContent(
|
|||
|
||||
@Composable
|
||||
private fun OverviewSection(
|
||||
data: StatsData.Overview,
|
||||
data: StatsData.MangaOverview,
|
||||
) {
|
||||
val none = stringResource(R.string.none)
|
||||
val context = LocalContext.current
|
||||
|
@ -85,7 +85,7 @@ private fun OverviewSection(
|
|||
|
||||
@Composable
|
||||
private fun TitlesStats(
|
||||
data: StatsData.Titles,
|
||||
data: StatsData.MangaTitles,
|
||||
) {
|
||||
StatsSection(R.string.label_titles_section) {
|
||||
Row {
|
|
@ -8,10 +8,18 @@ sealed class StatsScreenState {
|
|||
object Loading : StatsScreenState()
|
||||
|
||||
@Immutable
|
||||
data class Success(
|
||||
val overview: StatsData.Overview,
|
||||
val titles: StatsData.Titles,
|
||||
data class SuccessManga(
|
||||
val overview: StatsData.MangaOverview,
|
||||
val titles: StatsData.MangaTitles,
|
||||
val chapters: StatsData.Chapters,
|
||||
val trackers: StatsData.Trackers,
|
||||
) : 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 {
|
||||
|
||||
data class Overview(
|
||||
data class MangaOverview(
|
||||
val libraryMangaCount: Int,
|
||||
val completedMangaCount: Int,
|
||||
val totalReadDuration: Long,
|
||||
) : StatsData()
|
||||
|
||||
data class Titles(
|
||||
data class AnimeOverview(
|
||||
val libraryAnimeCount: Int,
|
||||
val completedAnimeCount: Int,
|
||||
val totalSeenDuration: Long,
|
||||
) : StatsData()
|
||||
|
||||
data class MangaTitles(
|
||||
val globalUpdateItemCount: Int,
|
||||
val startedMangaCount: Int,
|
||||
val localMangaCount: Int,
|
||||
) : StatsData()
|
||||
|
||||
data class AnimeTitles(
|
||||
val globalUpdateItemCount: Int,
|
||||
val startedAnimeCount: Int,
|
||||
val localAnimeCount: Int,
|
||||
) : StatsData()
|
||||
|
||||
data class Chapters(
|
||||
val totalChapterCount: Int,
|
||||
val readChapterCount: Int,
|
||||
val downloadCount: Int,
|
||||
) : StatsData()
|
||||
|
||||
data class Episodes(
|
||||
val totalEpisodeCount: Int,
|
||||
val readEpisodeCount: Int,
|
||||
val downloadCount: Int,
|
||||
) : StatsData()
|
||||
|
||||
data class Trackers(
|
||||
val trackedTitleCount: Int,
|
||||
val meanScore: Double,
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
package eu.kanade.presentation.updates
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
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.SnackbarHostState
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
@ -31,8 +25,8 @@ import eu.kanade.presentation.components.PullRefresh
|
|||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.ui.updates.UpdatesItem
|
||||
import eu.kanade.tachiyomi.ui.updates.UpdatesState
|
||||
import eu.kanade.tachiyomi.ui.updates.manga.UpdatesItem
|
||||
import eu.kanade.tachiyomi.ui.updates.manga.UpdatesState
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
@ -41,6 +35,7 @@ import kotlin.time.Duration.Companion.seconds
|
|||
fun UpdateScreen(
|
||||
state: UpdatesState,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
contentPadding: PaddingValues,
|
||||
lastUpdated: Long,
|
||||
relativeTime: Int,
|
||||
onClickCover: (UpdatesItem) -> Unit,
|
||||
|
@ -69,7 +64,7 @@ fun UpdateScreen(
|
|||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||
) { contentPadding ->
|
||||
) {
|
||||
when {
|
||||
state.isLoading -> LoadingScreen(modifier = Modifier.padding(contentPadding))
|
||||
state.items.isEmpty() -> EmptyScreen(
|
||||
|
|
|
@ -43,7 +43,7 @@ import eu.kanade.presentation.util.padding
|
|||
import eu.kanade.presentation.util.selectedBackground
|
||||
import eu.kanade.tachiyomi.R
|
||||
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 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.NetworkPreferences
|
||||
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.util.system.isDevFlavor
|
||||
import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory
|
||||
|
@ -194,6 +195,8 @@ class AppModule(val app: Application) : InjektModule {
|
|||
|
||||
addSingletonFactory { ImageSaver(app) }
|
||||
|
||||
addSingletonFactory { ExternalIntents() }
|
||||
|
||||
// Asynchronously init expensive components for a faster cold start
|
||||
ContextCompat.getMainExecutor(app).execute {
|
||||
get<NetworkHelper>()
|
||||
|
|
|
@ -22,7 +22,7 @@ import eu.kanade.tachiyomi.data.updater.AppUpdateJob
|
|||
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
|
||||
import eu.kanade.tachiyomi.network.NetworkPreferences
|
||||
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.ReaderPreferences
|
||||
import eu.kanade.tachiyomi.util.preference.minusAssign
|
||||
|
|
|
@ -60,7 +60,6 @@ class AnimeDownloadCache(
|
|||
.onStart { emit(Unit) }
|
||||
.shareIn(scope, SharingStarted.Eagerly, 1)
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -272,38 +271,38 @@ class AnimeDownloadCache(
|
|||
}?.id
|
||||
}
|
||||
|
||||
sourceDirs.values
|
||||
.map { sourceDir ->
|
||||
async {
|
||||
val animeDirs = sourceDir.dir.listFiles().orEmpty()
|
||||
.filterNot { it.name.isNullOrBlank() }
|
||||
.associate { it.name!! to AnimeDirectory(it) }
|
||||
sourceDirs.values
|
||||
.map { sourceDir ->
|
||||
async {
|
||||
val animeDirs = sourceDir.dir.listFiles().orEmpty()
|
||||
.filterNot { it.name.isNullOrBlank() }
|
||||
.associate { it.name!! to AnimeDirectory(it) }
|
||||
|
||||
sourceDir.animeDirs = ConcurrentHashMap(animeDirs)
|
||||
sourceDir.animeDirs = ConcurrentHashMap(animeDirs)
|
||||
|
||||
animeDirs.values.forEach { animeDir ->
|
||||
val episodeDirs = animeDir.dir.listFiles().orEmpty()
|
||||
.mapNotNull {
|
||||
when {
|
||||
// Ignore incomplete downloads
|
||||
it.name?.endsWith(AnimeDownloader.TMP_DIR_SUFFIX) == true -> null
|
||||
// Folder of images
|
||||
it.isDirectory -> it.name
|
||||
// MP4 files
|
||||
it.isFile && it.name?.endsWith(".mp4") == true -> it.name!!.substringBeforeLast(".mp4")
|
||||
// MKV files
|
||||
it.isFile && it.name?.endsWith(".mkv") == true -> it.name!!.substringBeforeLast(".mkv")
|
||||
// Anything else is irrelevant
|
||||
else -> null
|
||||
}
|
||||
animeDirs.values.forEach { animeDir ->
|
||||
val episodeDirs = animeDir.dir.listFiles().orEmpty()
|
||||
.mapNotNull {
|
||||
when {
|
||||
// Ignore incomplete downloads
|
||||
it.name?.endsWith(AnimeDownloader.TMP_DIR_SUFFIX) == true -> null
|
||||
// Folder of images
|
||||
it.isDirectory -> it.name
|
||||
// MP4 files
|
||||
it.isFile && it.name?.endsWith(".mp4") == true -> it.name!!.substringBeforeLast(".mp4")
|
||||
// MKV files
|
||||
it.isFile && it.name?.endsWith(".mkv") == true -> it.name!!.substringBeforeLast(".mkv")
|
||||
// Anything else is irrelevant
|
||||
else -> null
|
||||
}
|
||||
.toMutableSet()
|
||||
}
|
||||
.toMutableSet()
|
||||
|
||||
animeDir.episodeDirs = episodeDirs
|
||||
}
|
||||
animeDir.episodeDirs = episodeDirs
|
||||
}
|
||||
}
|
||||
.awaitAll()
|
||||
}
|
||||
.awaitAll()
|
||||
}.also {
|
||||
it.invokeOnCompletion(onCancelling = true) { exception ->
|
||||
if (exception != null && exception !is CancellationException) {
|
||||
|
|
|
@ -93,7 +93,6 @@ class AnimeDownloadManager(
|
|||
return queue.find { it.episode.id == episodeId }
|
||||
}
|
||||
|
||||
|
||||
fun startDownloadNow(episodeId: Long?) {
|
||||
if (episodeId == null) return
|
||||
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.withUIContext
|
||||
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.toFFmpegString
|
||||
import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||
|
@ -346,7 +345,7 @@ class AnimeDownloader(
|
|||
|
||||
val videoObservable = if (download.video == null) {
|
||||
// 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 ->
|
||||
if (video == null) {
|
||||
throw Exception(context.getString(R.string.video_list_empty_error))
|
||||
|
@ -458,13 +457,13 @@ class AnimeDownloader(
|
|||
video.videoUrl = file.uri.path
|
||||
video.progress = 100
|
||||
download.downloadedImages++
|
||||
video.status = Video.READY
|
||||
video.status = Video.State.READY
|
||||
}
|
||||
.map { video }
|
||||
// Mark this video as error and allow to download the remaining
|
||||
.onErrorReturn {
|
||||
video.progress = 0
|
||||
video.status = Video.ERROR
|
||||
video.status = Video.State.ERROR
|
||||
notifier.onError(it.message, download.episode.name, download.anime.title)
|
||||
video
|
||||
}
|
||||
|
@ -479,7 +478,7 @@ class AnimeDownloader(
|
|||
* @param filename the filename of the video.
|
||||
*/
|
||||
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
|
||||
var tries = 0
|
||||
return newObservable(video, download, tmpDir, filename)
|
||||
|
@ -632,7 +631,7 @@ class AnimeDownloader(
|
|||
* @param filename the filename of the video.
|
||||
*/
|
||||
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
|
||||
return Observable.just(tmpDir.createFile("$filename.mp4")).map {
|
||||
try {
|
||||
|
|
|
@ -4,7 +4,7 @@ import android.content.Context
|
|||
import android.net.Uri
|
||||
import eu.kanade.tachiyomi.R
|
||||
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.source.SourceManager
|
||||
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_TRACK
|
||||
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.full.models.BackupAnime
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupAnimeHistory
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupAnimeSource
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupCategory
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupHistory
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupManga
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupPreference
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupSerializer
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupSource
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BooleanPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.FloatPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.IntPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.LongPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.StringPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.StringSetPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.backupAnimeTrackMapper
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.backupCategoryMapper
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.backupChapterMapper
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.backupEpisodeMapper
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.backupTrackMapper
|
||||
import eu.kanade.tachiyomi.data.backup.models.Backup
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupAnime
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupAnimeHistory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupAnimeSource
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupManga
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupSerializer
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupSource
|
||||
import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.FloatPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.IntPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.LongPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.backupAnimeTrackMapper
|
||||
import eu.kanade.tachiyomi.data.backup.models.backupCategoryMapper
|
||||
import eu.kanade.tachiyomi.data.backup.models.backupChapterMapper
|
||||
import eu.kanade.tachiyomi.data.backup.models.backupEpisodeMapper
|
||||
import eu.kanade.tachiyomi.data.backup.models.backupTrackMapper
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
|
|
|
@ -4,21 +4,21 @@ import android.content.Context
|
|||
import android.net.Uri
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupAnime
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupAnimeHistory
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupAnimeSource
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupCategory
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupHistory
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupManga
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupPreference
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupSerializer
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BackupSource
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.BooleanPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.FloatPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.IntPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.LongPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.StringPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.full.models.StringSetPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupAnime
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupAnimeHistory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupAnimeSource
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupManga
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupSerializer
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupSource
|
||||
import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.FloatPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.IntPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.LongPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.database.models.Anime
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
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.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.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.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 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.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 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 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 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.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.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.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
|
||||
|
|
@ -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 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 kotlinx.serialization.Serializable
|
|
@ -29,7 +29,6 @@ interface Anime : SAnime {
|
|||
var skipIntroLength: Int
|
||||
get() = viewer_flags and 0x000000FF
|
||||
set(skipIntro) = setViewerFlags(skipIntro, 0x000000FF)
|
||||
|
||||
}
|
||||
|
||||
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.tachiyomi.animesource.AnimeSource
|
||||
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.
|
||||
|
|
|
@ -134,5 +134,4 @@ interface MangaTrackService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ import kotlinx.serialization.decodeFromString
|
|||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
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.track.model.Track as DomainTrack
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class OAuth(
|
||||
val access_token: String,
|
||||
|
|
|
@ -47,8 +47,8 @@ import eu.kanade.data.AnimeDatabaseHandler
|
|||
import eu.kanade.domain.anime.model.AnimeCover
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.core.security.SecurityPreferences
|
||||
import eu.kanade.tachiyomi.util.Constants
|
||||
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.system.dpToPx
|
||||
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.res.stringResource
|
||||
import androidx.core.net.toUri
|
||||
import cafe.adriel.voyager.core.model.coroutineScope
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.core.screen.Screen
|
||||
import cafe.adriel.voyager.core.screen.uniqueScreenKey
|
||||
|
@ -27,35 +26,35 @@ import cafe.adriel.voyager.navigator.Navigator
|
|||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import com.commandiron.wheel_picker_compose.WheelTextPicker
|
||||
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.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.DuplicateAnimeDialog
|
||||
import eu.kanade.presentation.components.LoadingScreen
|
||||
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.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.isTabletUi
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.animesource.AnimeSource
|
||||
import eu.kanade.tachiyomi.animesource.isLocalOrStub
|
||||
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateAnimeSearchScreen
|
||||
import eu.kanade.tachiyomi.ui.anime.track.AnimeTrackInfoDialogHomeScreen
|
||||
import eu.kanade.tachiyomi.ui.browse.animesource.browse.BrowseAnimeSourceScreen
|
||||
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.anime.track.AnimeTrackInfoDialogHomeScreen
|
||||
import eu.kanade.tachiyomi.ui.player.ExternalIntents
|
||||
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.util.lang.launchIO
|
||||
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.toast
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import logcat.LogPriority
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class AnimeScreen(
|
||||
|
@ -116,7 +112,12 @@ class AnimeScreen(
|
|||
snackbarHostState = screenModel.snackbarHostState,
|
||||
isTabletUi = isTabletUi(),
|
||||
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() },
|
||||
onAddToLibraryClicked = {
|
||||
screenModel.toggleFavorite()
|
||||
|
@ -128,14 +129,19 @@ class AnimeScreen(
|
|||
onTagClicked = { scope.launch { performGenreSearch(navigator, it, screenModel.source!!) } },
|
||||
onFilterButtonClicked = screenModel::showSettingsDialog,
|
||||
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) } },
|
||||
onCoverClicked = screenModel::showCoverDialog,
|
||||
onShareClicked = { shareAnime(context, screenModel.anime, screenModel.source) }.takeIf { isAnimeHttpSource },
|
||||
onDownloadActionClicked = screenModel::runDownloadAction.takeIf { !successState.source.isLocalOrStub() },
|
||||
onEditCategoryClicked = screenModel::promptChangeCategories.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,
|
||||
onMultiMarkAsSeenClicked = screenModel::markEpisodesSeen,
|
||||
onMarkPreviousAsSeenClicked = screenModel::markPreviousEpisodeSeen,
|
||||
|
@ -152,7 +158,7 @@ class AnimeScreen(
|
|||
ChangeCategoryDialog(
|
||||
initialSelection = dialog.initialSelection,
|
||||
onDismissRequest = onDismissRequest,
|
||||
onEditCategories = { navigator.push(AnimeCategoryScreen()) },
|
||||
onEditCategories = { navigator.push(CategoriesTab(false)) },
|
||||
onConfirm = { 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)
|
||||
}
|
||||
private fun openEpisodeInternal(context: Context, animeId: Long, episodeId: Long) {
|
||||
context.startActivity(PlayerActivity.newIntent(context, animeId, episodeId))
|
||||
}
|
||||
|
||||
// TODO: External Player support
|
||||
private fun openEpisode(context: Context, episode: Episode, alt: Boolean = false) {
|
||||
context.startActivity(PlayerActivity.newIntent(context, episode.animeId, episode.id))
|
||||
private suspend fun openEpisodeExternal(context: Context, animeId: Long, episodeId: Long) {
|
||||
context.startActivity(ExternalIntents.newIntent(context, animeId, episodeId))
|
||||
}
|
||||
|
||||
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? {
|
||||
|
@ -347,6 +364,7 @@ fun ChangeIntroLength(
|
|||
anime: Anime,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val setAnimeViewerFlags: SetAnimeViewerFlags by injectLazy()
|
||||
val titleText = R.string.action_change_intro_length
|
||||
var newLength = 0
|
||||
|
@ -364,10 +382,12 @@ fun ChangeIntroLength(
|
|||
)
|
||||
},
|
||||
onConfirm = {
|
||||
launchIO {
|
||||
scope.launchIO {
|
||||
setAnimeViewerFlags.awaitSetSkipIntroLength(anime.id, newLength.toLong())
|
||||
onDismissRequest()
|
||||
}},
|
||||
}
|
||||
Unit
|
||||
},
|
||||
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.UpdateAnime
|
||||
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.animetrack.interactor.GetAnimeTracks
|
||||
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.EpisodeUpdate
|
||||
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.ui.UiPreferences
|
||||
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.AnimeDownloadService
|
||||
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.EnhancedAnimeTrackService
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
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.shouldDownloadNewEpisodes
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
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 selectedEpisodeIds: HashSet<Long> = HashSet()
|
||||
|
||||
/**
|
||||
* 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()) {
|
||||
showTrackDialog()
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update favorite status of anime, (removes / adds) anime (to / from) library.
|
||||
*/
|
||||
|
@ -610,7 +609,6 @@ class AnimeInfoScreenModel(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
fun runEpisodeDownloadActions(
|
||||
items: List<EpisodeItem>,
|
||||
action: EpisodeDownloadAction,
|
||||
|
@ -789,7 +787,7 @@ class AnimeInfoScreenModel(
|
|||
TriStateFilter.ENABLED_NOT -> Anime.EPISODE_SHOW_NOT_BOOKMARKED
|
||||
}
|
||||
|
||||
coroutineScope.launchNonCancellable {
|
||||
coroutineScope.launchNonCancellable {
|
||||
setAnimeEpisodeFlags.awaitSetBookmarkFilter(anime, flag)
|
||||
}
|
||||
}
|
||||
|
@ -829,7 +827,6 @@ class AnimeInfoScreenModel(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
fun toggleSelection(
|
||||
item: EpisodeItem,
|
||||
selected: Boolean,
|
||||
|
@ -841,7 +838,7 @@ class AnimeInfoScreenModel(
|
|||
val selectedIndex = successState.processedEpisodes.indexOfFirst { it.episode.id == item.episode.id }
|
||||
if (selectedIndex < 0) return@apply
|
||||
|
||||
val selectedItem = get(selectedIndex )
|
||||
val selectedItem = get(selectedIndex)
|
||||
if ((selectedItem.selected && selected) || (!selectedItem.selected && !selected)) return@apply
|
||||
|
||||
val firstSelection = none { it.selected }
|
||||
|
@ -855,10 +852,10 @@ class AnimeInfoScreenModel(
|
|||
} else {
|
||||
// Try to select the items in-between when possible
|
||||
val range: IntRange
|
||||
if (selectedIndex < selectedPositions[0]) {
|
||||
range = selectedIndex + 1 until selectedPositions[0]
|
||||
if (selectedIndex < selectedPositions[0]) {
|
||||
range = selectedIndex + 1 until selectedPositions[0]
|
||||
selectedPositions[0] = selectedIndex
|
||||
} else if (selectedIndex > selectedPositions[1]) {
|
||||
} else if (selectedIndex > selectedPositions[1]) {
|
||||
range = (selectedPositions[1] + 1) until selectedIndex
|
||||
selectedPositions[1] = selectedIndex
|
||||
} else {
|
||||
|
@ -876,15 +873,15 @@ class AnimeInfoScreenModel(
|
|||
}
|
||||
} else if (userSelected && !fromLongPress) {
|
||||
if (!selected) {
|
||||
if (selectedIndex == selectedPositions[0]) {
|
||||
if (selectedIndex == selectedPositions[0]) {
|
||||
selectedPositions[0] = indexOfFirst { it.selected }
|
||||
} else if (selectedIndex == selectedPositions[1]) {
|
||||
} else if (selectedIndex == selectedPositions[1]) {
|
||||
selectedPositions[1] = indexOfLast { it.selected }
|
||||
}
|
||||
} else {
|
||||
if (selectedIndex < selectedPositions[0]) {
|
||||
if (selectedIndex < selectedPositions[0]) {
|
||||
selectedPositions[0] = selectedIndex
|
||||
} else if (selectedIndex > selectedPositions[1]) {
|
||||
} else if (selectedIndex > selectedPositions[1]) {
|
||||
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.Navigator
|
||||
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.GetAnimeWithEpisodes
|
||||
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.model.toDbTrack
|
||||
import eu.kanade.domain.animetrack.model.toDomainTrack
|
||||
import eu.kanade.domain.episode.interactor.SyncEpisodesWithTrackServiceTwoWay
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.presentation.anime.AnimeTrackInfoDialogHome
|
||||
import eu.kanade.presentation.anime.AnimeTrackServiceSearch
|
||||
import eu.kanade.presentation.components.AlertDialogContent
|
||||
import eu.kanade.presentation.manga.TrackChapterSelector
|
||||
import eu.kanade.presentation.manga.TrackDateSelector
|
||||
import eu.kanade.presentation.manga.TrackScoreSelector
|
||||
import eu.kanade.presentation.anime.AnimeTrackServiceSearch
|
||||
import eu.kanade.presentation.manga.TrackStatusSelector
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
||||
import eu.kanade.tachiyomi.data.database.models.AnimeTrack
|
||||
import eu.kanade.tachiyomi.data.track.EnhancedAnimeTrackService
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
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.util.lang.launchNonCancellable
|
||||
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.fastMapNotNull
|
||||
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.category.interactor.GetAnimeCategories
|
||||
import eu.kanade.domain.category.interactor.SetAnimeCategories
|
||||
import eu.kanade.domain.category.model.Category
|
||||
import eu.kanade.domain.episode.interactor.GetEpisodeByAnimeId
|
||||
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.sort
|
||||
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.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.model.SAnime
|
||||
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.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.launchNonCancellable
|
||||
|
@ -337,11 +338,11 @@ class AnimelibScreenModel(
|
|||
libraryPreferences.languageBadge().changes(),
|
||||
|
||||
preferences.downloadedOnly().changes(),
|
||||
libraryPreferences.filterDownloaded().changes(),
|
||||
libraryPreferences.filterUnread().changes(),
|
||||
libraryPreferences.filterStarted().changes(),
|
||||
libraryPreferences.filterBookmarked().changes(),
|
||||
libraryPreferences.filterCompleted().changes(),
|
||||
libraryPreferences.filterDownloadedAnime().changes(),
|
||||
libraryPreferences.filterUnseen().changes(),
|
||||
libraryPreferences.filterStartedAnime().changes(),
|
||||
libraryPreferences.filterBookmarkedAnime().changes(),
|
||||
libraryPreferences.filterCompletedAnime().changes(),
|
||||
transform = {
|
||||
ItemPreferences(
|
||||
downloadBadge = it[0] as Boolean,
|
||||
|
@ -406,10 +407,10 @@ class AnimelibScreenModel(
|
|||
* @return map of track id with the filter value
|
||||
*/
|
||||
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()) {
|
||||
val prefFlows = loggedServices
|
||||
.map { libraryPreferences.filterTracking(it.id.toInt()).changes() }
|
||||
.map { libraryPreferences.filterTrackingAnime(it.id.toInt()).changes() }
|
||||
.toTypedArray()
|
||||
combine(*prefFlows) {
|
||||
loggedServices
|
||||
|
|
|
@ -126,15 +126,15 @@ class AnimelibSettingsSheet(
|
|||
downloaded.state = State.INCLUDE.value
|
||||
downloaded.enabled = false
|
||||
} else {
|
||||
downloaded.state = libraryPreferences.filterDownloaded().get()
|
||||
downloaded.state = libraryPreferences.filterDownloadedAnime().get()
|
||||
}
|
||||
unseen.state = libraryPreferences.filterUnread().get()
|
||||
started.state = libraryPreferences.filterStarted().get()
|
||||
bookmarked.state = libraryPreferences.filterBookmarked().get()
|
||||
completed.state = libraryPreferences.filterCompleted().get()
|
||||
unseen.state = libraryPreferences.filterUnseen().get()
|
||||
started.state = libraryPreferences.filterStartedAnime().get()
|
||||
bookmarked.state = libraryPreferences.filterBookmarkedAnime().get()
|
||||
completed.state = libraryPreferences.filterCompletedAnime().get()
|
||||
|
||||
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
|
||||
when (item) {
|
||||
downloaded -> libraryPreferences.filterDownloaded().set(newState)
|
||||
unseen -> libraryPreferences.filterUnread().set(newState)
|
||||
started -> libraryPreferences.filterStarted().set(newState)
|
||||
bookmarked -> libraryPreferences.filterBookmarked().set(newState)
|
||||
completed -> libraryPreferences.filterCompleted().set(newState)
|
||||
downloaded -> libraryPreferences.filterDownloadedAnime().set(newState)
|
||||
unseen -> libraryPreferences.filterUnseen().set(newState)
|
||||
started -> libraryPreferences.filterStartedAnime().set(newState)
|
||||
bookmarked -> libraryPreferences.filterBookmarkedAnime().set(newState)
|
||||
completed -> libraryPreferences.filterCompletedAnime().set(newState)
|
||||
else -> {
|
||||
trackFilters.forEach { trackFilter ->
|
||||
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
|
||||
|
||||
import android.content.Context
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.graphics.res.animatedVectorResource
|
||||
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.tab.LocalTabNavigator
|
||||
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.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.DeleteAnimelibAnimeDialog
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.components.EmptyScreenAction
|
||||
import eu.kanade.presentation.components.AnimelibBottomActionMenu
|
||||
import eu.kanade.presentation.components.LoadingScreen
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.animelib.components.AnimelibContent
|
||||
import eu.kanade.presentation.library.components.LibraryToolbar
|
||||
import eu.kanade.presentation.manga.components.DownloadCustomAmountDialog
|
||||
import eu.kanade.presentation.util.Tab
|
||||
import eu.kanade.tachiyomi.R
|
||||
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.category.CategoryScreen
|
||||
import eu.kanade.tachiyomi.ui.category.CategoriesTab
|
||||
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.player.ExternalIntents
|
||||
import eu.kanade.tachiyomi.ui.player.PlayerActivity
|
||||
import eu.kanade.tachiyomi.ui.player.settings.PlayerPreferences
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
@ -72,7 +76,7 @@ object AnimelibTab : Tab {
|
|||
R.string.label_animelib
|
||||
}
|
||||
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(
|
||||
index = 0u,
|
||||
title = stringResource(title),
|
||||
|
@ -108,6 +112,23 @@ object AnimelibTab : Tab {
|
|||
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(
|
||||
topBar = { scrollBehavior ->
|
||||
val title = state.getToolbarTitle(
|
||||
|
@ -183,13 +204,8 @@ object AnimelibTab : Tab {
|
|||
onContinueWatchingClicked = { it: AnimelibAnime ->
|
||||
scope.launchIO {
|
||||
val episode = screenModel.getNextUnseenEpisode(it.anime)
|
||||
if (episode != null) {
|
||||
context.startActivity(PlayerActivity.newIntent(context, episode.animeId, episode.id))
|
||||
} else {
|
||||
snackbarHostState.showSnackbar(context.getString(R.string.no_next_episode))
|
||||
}
|
||||
if (episode != null) openEpisode(episode)
|
||||
}
|
||||
// TODO: External Intent
|
||||
Unit
|
||||
}.takeIf { state.showAnimeContinueButton },
|
||||
onToggleSelection = { screenModel.toggleSelection(it) },
|
||||
|
@ -217,7 +233,7 @@ object AnimelibTab : Tab {
|
|||
onDismissRequest = onDismissRequest,
|
||||
onEditCategories = {
|
||||
screenModel.clearSelection()
|
||||
navigator.push(CategoryScreen())
|
||||
navigator.push(CategoriesTab(false))
|
||||
},
|
||||
onConfirm = { include, exclude ->
|
||||
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 image = AnimatedImageVector.animatedVectorResource(R.drawable.anim_browse_enter)
|
||||
return TabOptions(
|
||||
index = 6u,
|
||||
index = 3u,
|
||||
title = stringResource(R.string.browse),
|
||||
icon = rememberAnimatedVectorPainter(image, isSelected),
|
||||
)
|
||||
|
@ -68,6 +68,7 @@ data class BrowseTab(
|
|||
onChangeSearchQuery = extensionsScreenModel::search,
|
||||
searchQueryAnime = animeExtensionsQuery,
|
||||
onChangeSearchQueryAnime = animeExtensionsScreenModel::search,
|
||||
scrollable = true,
|
||||
)
|
||||
|
||||
// For local source
|
||||
|
|
|
@ -220,7 +220,6 @@ data class AnimeExtensionsState(
|
|||
val isEmpty = items.isEmpty()
|
||||
}
|
||||
|
||||
|
||||
sealed interface AnimeExtensionUiModel {
|
||||
sealed interface Header : AnimeExtensionUiModel {
|
||||
data class Resource(@StringRes val textRes: Int) : Header
|
||||
|
|
|
@ -21,189 +21,7 @@ data class AnimeExtensionDetailsScreen(
|
|||
@Composable
|
||||
override fun Content() {
|
||||
val context = LocalContext.current
|
||||
val screenModel = rememberScreenModel { AnimeEpackage eu.kanade.tachiyomi.ui.browse.extension.details
|
||||
|
||||
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 screenModel = rememberScreenModel { AnimeExtensionDetailsScreenModel(pkgName = pkgName, context = context) }
|
||||
val state by screenModel.state.collectAsState()
|
||||
|
||||
if (state.isLoading) {
|
||||
|
|
|
@ -41,9 +41,10 @@ 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.animesource.ConfigurableAnimeSource
|
||||
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.widget.TachiyomiTextInputEditText.Companion.setIncognito
|
||||
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.coroutineScope
|
||||
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.model.AnimeSource
|
||||
import eu.kanade.domain.source.interactor.ToggleLanguage
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
@ -27,7 +27,7 @@ class AnimeSourcesFilterScreenModel(
|
|||
combine(
|
||||
getLanguagesWithSources.subscribe(),
|
||||
preferences.enabledLanguages().changes(),
|
||||
preferences.disabledSources().changes(),
|
||||
preferences.disabledAnimeSources().changes(),
|
||||
) { a, b, c -> Triple(a, b, c) }
|
||||
.catch { throwable ->
|
||||
mutableState.update {
|
||||
|
|
|
@ -50,9 +50,9 @@ import eu.kanade.presentation.util.AssistContentScreen
|
|||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.animesource.LocalAnimeSource
|
||||
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.browse.animesource.browse.BrowseAnimeSourceScreenModel.Listing
|
||||
import eu.kanade.tachiyomi.ui.category.CategoriesTab
|
||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||
import eu.kanade.tachiyomi.util.Constants
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
|
@ -242,7 +242,7 @@ data class BrowseAnimeSourceScreen(
|
|||
ChangeCategoryDialog(
|
||||
initialSelection = dialog.initialSelection,
|
||||
onDismissRequest = onDismissRequest,
|
||||
onEditCategories = { navigator.push(AnimeCategoryScreen()) },
|
||||
onEditCategories = { navigator.push(CategoriesTab(false)) },
|
||||
onConfirm = { include, _ ->
|
||||
screenModel.changeAnimeFavorite(dialog.anime)
|
||||
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.NetworkToLocalAnime
|
||||
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.toAnimeUpdate
|
||||
import eu.kanade.domain.anime.model.toDomainAnime
|
||||
import eu.kanade.domain.animesource.interactor.GetRemoteAnime
|
||||
import eu.kanade.domain.animetrack.interactor.InsertAnimeTrack
|
||||
|
@ -299,10 +299,12 @@ class BrowseAnimeSourceScreenModel(
|
|||
// Choose a category
|
||||
else -> {
|
||||
val preselectedIds = getCategories.await(anime.id).map { it.id }
|
||||
setDialog(Dialog.ChangeAnimeCategory(
|
||||
anime,
|
||||
categories.mapAsCheckboxState { it.id in preselectedIds },
|
||||
))
|
||||
setDialog(
|
||||
Dialog.ChangeAnimeCategory(
|
||||
anime,
|
||||
categories.mapAsCheckboxState { it.id in preselectedIds },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -320,7 +322,7 @@ class BrowseAnimeSourceScreenModel(
|
|||
insertTrack.await(track.toDomainTrack()!!)
|
||||
|
||||
val chapters = getEpisodeByAnimeId.await(anime.id)
|
||||
syncEpisodesWithTrackServiceTwoWay.await(chapters, track.toDomainTrack()!!, service.animeService )
|
||||
syncEpisodesWithTrackServiceTwoWay.await(chapters, track.toDomainTrack()!!, service.animeService)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
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.UpdateAnime
|
||||
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.toDomainAnime
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.tachiyomi.animeextension.AnimeExtensionManager
|
||||
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
|
||||
|
@ -42,7 +42,7 @@ abstract class AnimeSearchScreenModel<T>(
|
|||
protected lateinit var extensionFilter: String
|
||||
|
||||
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> ->
|
||||
compareBy<AnimeCatalogueSource>(
|
||||
|
@ -95,8 +95,8 @@ abstract class AnimeSearchScreenModel<T>(
|
|||
val enabledSources = getEnabledSources()
|
||||
|
||||
if (filter.isEmpty()) {
|
||||
val shouldSearchPinnedOnly = sourcePreferences.searchPinnedSourcesOnly().get()
|
||||
val pinnedSources = sourcePreferences.pinnedSources().get()
|
||||
val shouldSearchPinnedOnly = sourcePreferences.searchPinnedAnimeSourcesOnly().get()
|
||||
val pinnedSources = sourcePreferences.pinnedAnimeSources().get()
|
||||
|
||||
return enabledSources.filter {
|
||||
if (shouldSearchPinnedOnly) {
|
||||
|
|
|
@ -9,8 +9,8 @@ import cafe.adriel.voyager.core.screen.uniqueScreenKey
|
|||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
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.browse.animesource.browse.BrowseAnimeSourceScreen
|
||||
|
||||
class GlobalAnimeSearchScreen(
|
||||
val searchQuery: String = "",
|
||||
|
|
|
@ -18,7 +18,7 @@ class GlobalAnimeSearchScreenModel(
|
|||
) : AnimeSearchScreenModel<GlobalAnimeSearchState>(GlobalAnimeSearchState(searchQuery = initialQuery)) {
|
||||
|
||||
val incognitoMode = preferences.incognitoMode()
|
||||
val lastUsedSourceId = sourcePreferences.lastUsedSource()
|
||||
val lastUsedSourceId = sourcePreferences.lastUsedAnimeSource()
|
||||
|
||||
init {
|
||||
extensionFilter = initialExtensionFilter
|
||||
|
|
|
@ -12,8 +12,8 @@ import cafe.adriel.voyager.navigator.currentOrThrow
|
|||
import eu.kanade.presentation.animebrowse.MigrateAnimeScreen
|
||||
import eu.kanade.presentation.components.LoadingScreen
|
||||
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.browse.migration.search.MigrateAnimeSearchScreen
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
||||
|
@ -38,7 +38,7 @@ data class MigrationAnimeScreen(
|
|||
navigateUp = navigator::pop,
|
||||
title = state.source!!.name,
|
||||
state = state,
|
||||
onClickItem = { navigator.push(MigrateSearchScreen(it.id)) },
|
||||
onClickItem = { navigator.push(MigrateAnimeSearchScreen(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.coroutineScope
|
||||
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.source.interactor.SetMigrateSorting
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
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.animesource.LocalAnimeSource
|
||||
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.home.HomeScreen
|
||||
import eu.kanade.tachiyomi.ui.anime.AnimeScreen
|
||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||
import eu.kanade.tachiyomi.util.Constants
|
||||
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.navigator.LocalNavigator
|
||||
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.model.Anime
|
||||
import eu.kanade.domain.anime.model.AnimeUpdate
|
||||
import eu.kanade.domain.anime.model.hasCustomCover
|
||||
import eu.kanade.domain.animetrack.interactor.GetAnimeTracks
|
||||
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.SyncEpisodesWithSource
|
||||
import eu.kanade.domain.episode.interactor.UpdateEpisode
|
||||
import eu.kanade.domain.episode.model.toEpisodeUpdate
|
||||
import eu.kanade.presentation.animebrowse.MigrateAnimeSearchScreen
|
||||
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.AnimeSourceManager
|
||||
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.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.browse.migration.AnimeMigrationFlags
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import uy.kohesive.injekt.Injekt
|
||||
|
|
|
@ -2,9 +2,9 @@ package eu.kanade.tachiyomi.ui.browse.migration.search
|
|||
|
||||
import androidx.compose.runtime.Immutable
|
||||
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.model.Anime
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
|
||||
import eu.kanade.tachiyomi.animesource.AnimeSourceManager
|
||||
|
@ -38,7 +38,7 @@ class MigrateAnimeSearchScreenModel(
|
|||
}
|
||||
|
||||
val incognitoMode = preferences.incognitoMode()
|
||||
val lastUsedSourceId = sourcePreferences.lastUsedSource()
|
||||
val lastUsedSourceId = sourcePreferences.lastUsedAnimeSource()
|
||||
|
||||
override fun getEnabledSources(): List<AnimeCatalogueSource> {
|
||||
val enabledLanguages = sourcePreferences.enabledLanguages().get()
|
||||
|
|
|
@ -25,7 +25,7 @@ fun Screen.migrateSourceTab(): TabContent {
|
|||
val state by screenModel.state.collectAsState()
|
||||
|
||||
return TabContent(
|
||||
titleRes = R.string.label_migration_manga,
|
||||
titleRes = R.string.label_migration,
|
||||
actions = listOf(
|
||||
AppBar.Action(
|
||||
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.online.HttpSource
|
||||
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.webview.WebViewActivity
|
||||
import eu.kanade.tachiyomi.util.Constants
|
||||
|
@ -242,7 +242,7 @@ data class BrowseSourceScreen(
|
|||
ChangeCategoryDialog(
|
||||
initialSelection = dialog.initialSelection,
|
||||
onDismissRequest = onDismissRequest,
|
||||
onEditCategories = { navigator.push(CategoryScreen()) },
|
||||
onEditCategories = { navigator.push(CategoriesTab(true)) },
|
||||
onConfirm = { include, _ ->
|
||||
screenModel.changeMangaFavorite(dialog.manga)
|
||||
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