Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Observe tracker login state instead of fetching once #987

Merged
merged 2 commits into from
Jul 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.FilterChip
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
Expand Down Expand Up @@ -125,7 +126,7 @@ private fun ColumnScope.FilterPage(
)
}

val trackers = remember { screenModel.trackers }
val trackers by screenModel.trackersFlow.collectAsState()
when (trackers.size) {
0 -> {
// No trackers
Expand Down Expand Up @@ -158,15 +159,15 @@ private fun ColumnScope.SortPage(
category: Category?,
screenModel: LibrarySettingsScreenModel,
) {
val trackers by screenModel.trackersFlow.collectAsState()
val sortingMode = category.sort.type
val sortDescending = !category.sort.isAscending

val trackerSortOption =
if (screenModel.trackers.isEmpty()) {
emptyList()
} else {
listOf(MR.strings.action_sort_tracker_score to LibrarySort.Type.TrackerMean)
}
val trackerSortOption = if (trackers.isEmpty()) {
emptyList()
} else {
listOf(MR.strings.action_sort_tracker_score to LibrarySort.Type.TrackerMean)
}

listOf(
MR.strings.action_sort_alpha to LibrarySort.Type.Alphabetical,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.structuralEqualityPolicy
import androidx.compose.ui.unit.dp
import eu.kanade.domain.track.service.TrackPreferences
import eu.kanade.presentation.more.settings.widget.EditTextPreferenceWidget
import eu.kanade.presentation.more.settings.widget.InfoWidget
import eu.kanade.presentation.more.settings.widget.ListPreferenceWidget
Expand All @@ -23,8 +23,6 @@ import eu.kanade.presentation.more.settings.widget.TrackingPreferenceWidget
import kotlinx.coroutines.launch
import tachiyomi.presentation.core.components.SliderItem
import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get

val LocalPreferenceHighlighted = compositionLocalOf(structuralEqualityPolicy()) { false }
val LocalPreferenceMinHeight = compositionLocalOf(structuralEqualityPolicy()) { 56.dp }
Expand Down Expand Up @@ -156,16 +154,14 @@ internal fun PreferenceItem(
)
}
is Preference.PreferenceItem.TrackerPreference -> {
val uName by Injekt.get<TrackPreferences>()
.trackUsername(item.tracker)
.collectAsState()
item.tracker.run {
TrackingPreferenceWidget(
tracker = this,
checked = uName.isNotEmpty(),
onClick = { if (isLoggedIn) item.logout() else item.login() },
)
val isLoggedIn by item.tracker.let { tracker ->
tracker.isLoggedInFlow.collectAsState(tracker.isLoggedIn)
}
TrackingPreferenceWidget(
tracker = item.tracker,
checked = isLoggedIn,
onClick = { if (isLoggedIn) item.logout() else item.login() },
)
}
is Preference.PreferenceItem.InfoPreference -> {
InfoWidget(text = item.title)
Expand Down
11 changes: 11 additions & 0 deletions app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import eu.kanade.domain.track.service.TrackPreferences
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import logcat.LogPriority
import okhttp3.OkHttpClient
import tachiyomi.core.common.util.lang.withIOContext
Expand Down Expand Up @@ -53,6 +55,15 @@ abstract class BaseTracker(
get() = getUsername().isNotEmpty() &&
getPassword().isNotEmpty()

override val isLoggedInFlow: Flow<Boolean> by lazy {
combine(
trackPreferences.trackUsername(this).changes(),
trackPreferences.trackPassword(this).changes(),
) { username, password ->
username.isNotEmpty() && password.isNotEmpty()
}
}

override fun getUsername() = trackPreferences.trackUsername(this).get()

override fun getPassword() = trackPreferences.trackPassword(this).get()
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import dev.icerock.moko.resources.StringResource
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.flow.Flow
import okhttp3.OkHttpClient
import tachiyomi.domain.track.model.Track as DomainTrack

Expand Down Expand Up @@ -61,6 +62,8 @@ interface Tracker {

val isLoggedIn: Boolean

val isLoggedInFlow: Flow<Boolean>

fun getUsername(): String

fun getPassword(): String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.data.track.mangaupdates.MangaUpdates
import eu.kanade.tachiyomi.data.track.myanimelist.MyAnimeList
import eu.kanade.tachiyomi.data.track.shikimori.Shikimori
import eu.kanade.tachiyomi.data.track.suwayomi.Suwayomi
import kotlinx.coroutines.flow.combine

class TrackerManager {

Expand All @@ -32,5 +33,13 @@ class TrackerManager {

fun loggedInTrackers() = trackers.filter { it.isLoggedIn }

fun loggedInTrackersFlow() = combine(trackers.map { it.isLoggedInFlow }) {
it.mapIndexedNotNull { index, isLoggedIn ->
if (isLoggedIn) trackers[index] else null
}
}

fun get(id: Long) = trackers.find { it.id == id }

fun getAll(ids: Set<Long>) = trackers.filter { it.id in ids }
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
Expand Down Expand Up @@ -106,10 +107,10 @@ class LibraryScreenModel(
getTracksPerManga.subscribe(),
getTrackingFilterFlow(),
downloadCache.changes,
) { searchQuery, library, tracks, loggedInTrackers, _ ->
) { searchQuery, library, tracks, trackingFiler, _ ->
library
.applyFilters(tracks, loggedInTrackers)
.applySort(tracks)
.applyFilters(tracks, trackingFiler)
.applySort(tracks, trackingFiler.keys)
.mapValues { (_, value) ->
if (searchQuery != null) {
// Filter query
Expand Down Expand Up @@ -173,9 +174,10 @@ class LibraryScreenModel(
/**
* Applies library filters to the given map of manga.
*/
@Suppress("LongMethod", "CyclomaticComplexMethod")
private suspend fun LibraryMap.applyFilters(
trackMap: Map<Long, List<Track>>,
loggedInTrackers: Map<Long, TriState>,
trackingFiler: Map<Long, TriState>,
): LibraryMap {
val prefs = getLibraryItemPreferencesFlow().first()
val downloadedOnly = prefs.globalFilterDownloaded
Expand All @@ -187,10 +189,10 @@ class LibraryScreenModel(
val filterCompleted = prefs.filterCompleted
val filterIntervalCustom = prefs.filterIntervalCustom

val isNotLoggedInAnyTrack = loggedInTrackers.isEmpty()
val isNotLoggedInAnyTrack = trackingFiler.isEmpty()

val excludedTracks = loggedInTrackers.mapNotNull { if (it.value == TriState.ENABLED_NOT) it.key else null }
val includedTracks = loggedInTrackers.mapNotNull { if (it.value == TriState.ENABLED_IS) it.key else null }
val excludedTracks = trackingFiler.mapNotNull { if (it.value == TriState.ENABLED_NOT) it.key else null }
val includedTracks = trackingFiler.mapNotNull { if (it.value == TriState.ENABLED_IS) it.key else null }
val trackFiltersIsIgnored = includedTracks.isEmpty() && excludedTracks.isEmpty()

val filterFnDownloaded: (LibraryItem) -> Boolean = {
Expand Down Expand Up @@ -254,17 +256,19 @@ class LibraryScreenModel(
/**
* Applies library sorting to the given map of manga.
*/
@Suppress("LongMethod", "CyclomaticComplexMethod")
private fun LibraryMap.applySort(
// Map<MangaId, List<Track>>
trackMap: Map<Long, List<Track>>,
loggedInTrackerIds: Set<Long>
): LibraryMap {
val sortAlphabetically: (LibraryItem, LibraryItem) -> Int = { i1, i2 ->
i1.libraryManga.manga.title.lowercase().compareToWithCollator(i2.libraryManga.manga.title.lowercase())
}

val defaultTrackerScoreSortValue = -1.0
val trackerScores by lazy {
val trackerMap = trackerManager.loggedInTrackers().associateBy { e -> e.id }
val trackerMap = trackerManager.getAll(loggedInTrackerIds).associateBy { e -> e.id }
trackMap.mapValues { entry ->
when {
entry.value.isEmpty() -> null
Expand Down Expand Up @@ -405,18 +409,17 @@ class LibraryScreenModel(
* @return map of track id with the filter value
*/
private fun getTrackingFilterFlow(): Flow<Map<Long, TriState>> {
val loggedInTrackers = trackerManager.loggedInTrackers()
return if (loggedInTrackers.isNotEmpty()) {
val prefFlows = loggedInTrackers
.map { libraryPreferences.filterTracking(it.id.toInt()).changes() }
.toTypedArray()
combine(*prefFlows) {
return trackerManager.loggedInTrackersFlow().flatMapLatest { loggedInTrackers ->
if (loggedInTrackers.isEmpty()) return@flatMapLatest flowOf(emptyMap())

val prefFlows = loggedInTrackers.map { tracker ->
libraryPreferences.filterTracking(tracker.id.toInt()).changes()
}
combine(prefFlows) {
loggedInTrackers
.mapIndexed { index, tracker -> tracker.id to it[index] }
.toMap()
}
} else {
flowOf(emptyMap())
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.data.track.TrackerManager
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.TriState
import tachiyomi.core.common.preference.getAndSet
Expand All @@ -16,17 +18,22 @@ import tachiyomi.domain.library.model.LibrarySort
import tachiyomi.domain.library.service.LibraryPreferences
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.time.Duration.Companion.seconds

class LibrarySettingsScreenModel(
val preferences: BasePreferences = Injekt.get(),
val libraryPreferences: LibraryPreferences = Injekt.get(),
private val setDisplayMode: SetDisplayMode = Injekt.get(),
private val setSortModeForCategory: SetSortModeForCategory = Injekt.get(),
private val trackerManager: TrackerManager = Injekt.get(),
trackerManager: TrackerManager = Injekt.get(),
) : ScreenModel {

val trackers
get() = trackerManager.trackers.filter { it.isLoggedIn }
val trackersFlow = trackerManager.loggedInTrackersFlow()
.stateIn(
scope = screenModelScope,
started = SharingStarted.WhileSubscribed(5.seconds.inWholeMilliseconds),
initialValue = trackerManager.loggedInTrackers()
)

fun toggleFilter(preference: (LibraryPreferences) -> Preference<TriState>) {
preference(libraryPreferences).getAndSet {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ class MangaScreen(
)
}.takeIf { isHttpSource },
onTrackingClicked = {
if (screenModel.loggedInTrackers.isEmpty()) {
if (successState.loggedInTracker.isEmpty()) {
navigator.push(SettingsScreen(SettingsScreen.Destination.Tracking))
} else {
screenModel.showTrackDialog()
Expand Down
34 changes: 22 additions & 12 deletions app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import eu.kanade.tachiyomi.data.download.DownloadCache
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.track.EnhancedTracker
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.network.HttpException
import eu.kanade.tachiyomi.source.Source
Expand All @@ -45,7 +46,6 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -117,8 +117,6 @@ class MangaScreenModel(
private val successState: State.Success?
get() = state.value as? State.Success

val loggedInTrackers by lazy { trackerManager.trackers.filter { it.isLoggedIn } }

val manga: Manga?
get() = successState?.manga

Expand Down Expand Up @@ -194,6 +192,16 @@ class MangaScreenModel(
}
}

screenModelScope.launchIO {
trackerManager.loggedInTrackersFlow()
.distinctUntilChanged()
.collectLatest { trackers ->
updateSuccessState {
it.copy(loggedInTracker = trackers)
}
}
}

observeDownloads()

screenModelScope.launchIO {
Expand Down Expand Up @@ -978,15 +986,16 @@ class MangaScreenModel(
val manga = successState?.manga ?: return

screenModelScope.launchIO {
getTracks.subscribe(manga.id)
.catch { logcat(LogPriority.ERROR, it) }
.map { tracks ->
loggedInTrackers
// Map to TrackItem
.map { service -> TrackItem(tracks.find { it.trackerId == service.id }, service) }
// Show only if the service supports this manga's source
.filter { (it.tracker as? EnhancedTracker)?.accept(source!!) ?: true }
}
combine(
getTracks.subscribe(manga.id).catch { logcat(LogPriority.ERROR, it) },
trackerManager.loggedInTrackersFlow(),
) { mangaTracks, loggedInTrackers ->
loggedInTrackers
// Map to TrackItem
.map { service -> TrackItem(mangaTracks.find { it.trackerId == service.id }, service) }
// Show only if the service supports this manga's source
.filter { (it.tracker as? EnhancedTracker)?.accept(source!!) ?: true }
}
.distinctUntilChanged()
.collectLatest { trackItems ->
updateSuccessState { it.copy(trackItems = trackItems) }
Expand Down Expand Up @@ -1057,6 +1066,7 @@ class MangaScreenModel(
val isRefreshingData: Boolean = false,
val dialog: Dialog? = null,
val hasPromptedToAddBefore: Boolean = false,
val loggedInTracker: List<Tracker> = emptyList(),
) : State {
val processedChapters by lazy {
chapters.applyFilters(manga).toList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ data class TrackInfoDialogHomeScreen(
}

private fun List<Track>.mapToTrackItem(): List<TrackItem> {
val loggedInTrackers = Injekt.get<TrackerManager>().trackers.filter { it.isLoggedIn }
val loggedInTrackers = Injekt.get<TrackerManager>().loggedInTrackers()
val source = Injekt.get<SourceManager>().getOrStub(sourceId)
return loggedInTrackers
// Map to TrackItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class StatsScreenModel(
private val trackerManager: TrackerManager = Injekt.get(),
) : StateScreenModel<StatsScreenState>(StatsScreenState.Loading) {

private val loggedInTrackers by lazy { trackerManager.trackers.fastFilter { it.isLoggedIn } }
private val loggedInTrackers by lazy { trackerManager.loggedInTrackers() }

init {
screenModelScope.launchIO {
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/eu/kanade/test/DummyTracker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import okhttp3.OkHttpClient
import tachiyomi.domain.track.model.Track
import tachiyomi.i18n.MR
Expand All @@ -16,6 +18,7 @@ data class DummyTracker(
override val name: String,
override val supportsReadingDates: Boolean = false,
override val isLoggedIn: Boolean = false,
override val isLoggedInFlow: Flow<Boolean> = flowOf(false),
val valLogoColor: Int = Color.rgb(18, 25, 35),
val valLogo: Int = R.drawable.ic_tracker_anilist,
val valStatuses: List<Long> = (1L..6L).toList(),
Expand Down