Fix tracker for multiple branches

This commit is contained in:
Koitharu
2022-08-04 11:32:50 +03:00
parent 14f5d5daa4
commit ffbe05b2ae
10 changed files with 113 additions and 82 deletions

View File

@@ -15,5 +15,6 @@ disabled_rules=no-wildcard-imports,no-unused-imports
ij_continuation_indent_size = 4 ij_continuation_indent_size = 4
[{*.kt,*.kts}] [{*.kt,*.kts}]
ij_kotlin_allow_trailing_comma_on_call_site = true
ij_kotlin_allow_trailing_comma = true ij_kotlin_allow_trailing_comma = true
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL

View File

@@ -1,6 +1,34 @@
package org.koitharu.kotatsu.core.model package org.koitharu.kotatsu.core.model
import androidx.core.os.LocaleListCompat
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.util.mapToSet import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.toTitleCase
import org.koitharu.kotatsu.utils.ext.iterator
fun Collection<Manga>.ids() = mapToSet { it.id } fun Collection<Manga>.ids() = mapToSet { it.id }
fun Manga.getPreferredBranch(history: MangaHistory?): String? {
val ch = chapters
if (ch.isNullOrEmpty()) {
return null
}
if (history != null) {
val currentChapter = ch.find { it.id == history.chapterId }
if (currentChapter != null) {
return currentChapter.branch
}
}
val groups = ch.groupBy { it.branch }
for (locale in LocaleListCompat.getAdjustedDefault()) {
var language = locale.getDisplayLanguage(locale).toTitleCase(locale)
if (groups.containsKey(language)) {
return language
}
language = locale.getDisplayName(locale).toTitleCase(locale)
if (groups.containsKey(language)) {
return language
}
}
return groups.maxByOrNull { it.value.size }?.key
}

View File

@@ -105,7 +105,7 @@ class DetailsActivity :
Toast.makeText( Toast.makeText(
this, this,
getString(R.string._s_deleted_from_local_storage, manga.title), getString(R.string._s_deleted_from_local_storage, manga.title),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT,
).show() ).show()
finishAfterTransition() finishAfterTransition()
} }
@@ -131,7 +131,7 @@ class DetailsActivity :
onActionClick = { onActionClick = {
e.report("DetailsActivity::onError") e.report("DetailsActivity::onError")
dismiss() dismiss()
} },
) )
} }
else -> { else -> {
@@ -142,14 +142,14 @@ class DetailsActivity :
override fun onWindowInsetsChanged(insets: Insets) { override fun onWindowInsetsChanged(insets: Insets) {
binding.snackbar.updatePadding( binding.snackbar.updatePadding(
bottom = insets.bottom bottom = insets.bottom,
) )
binding.toolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> { binding.toolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = insets.top topMargin = insets.top
} }
binding.root.updatePadding( binding.root.updatePadding(
left = insets.left, left = insets.left,
right = insets.right right = insets.right,
) )
} }
@@ -159,6 +159,7 @@ class DetailsActivity :
tab.removeBadge() tab.removeBadge()
} else { } else {
val badge = tab.orCreateBadge val badge = tab.orCreateBadge
badge.maxCharacterCount = 3
badge.number = newChapters badge.number = newChapters
badge.isVisible = true badge.isVisible = true
} }
@@ -275,8 +276,8 @@ class DetailsActivity :
ReaderActivity.newIntent( ReaderActivity.newIntent(
context = this@DetailsActivity, context = this@DetailsActivity,
manga = remoteManga, manga = remoteManga,
state = ReaderState(chapterId, 0, 0) state = ReaderState(chapterId, 0, 0),
) ),
) )
} }
setNeutralButton(R.string.download) { _, _ -> setNeutralButton(R.string.download) { _, _ ->
@@ -350,8 +351,8 @@ class DetailsActivity :
dialogBuilder.setMessage( dialogBuilder.setMessage(
getString( getString(
R.string.large_manga_save_confirm, R.string.large_manga_save_confirm,
resources.getQuantityString(R.plurals.chapters, chaptersCount, chaptersCount) resources.getQuantityString(R.plurals.chapters, chaptersCount, chaptersCount),
) ),
).setPositiveButton(R.string.save) { _, _ -> ).setPositiveButton(R.string.save) { _, _ ->
DownloadService.start(this, manga) DownloadService.start(this, manga)
} }

View File

@@ -2,7 +2,6 @@ package org.koitharu.kotatsu.details.ui
import android.app.ActivityOptions import android.app.ActivityOptions
import android.os.Bundle import android.os.Bundle
import android.text.Spanned
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.view.* import android.view.*
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
@@ -10,18 +9,15 @@ import androidx.core.content.ContextCompat
import androidx.core.graphics.Insets import androidx.core.graphics.Insets
import androidx.core.net.toFile import androidx.core.net.toFile
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.text.parseAsHtml
import androidx.core.view.MenuProvider import androidx.core.view.MenuProvider
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import coil.ImageLoader import coil.ImageLoader
import coil.request.ImageRequest import coil.request.ImageRequest
import coil.size.Scale
import coil.util.CoilUtils import coil.util.CoilUtils
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.android.ext.android.get
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
@@ -33,6 +29,7 @@ import org.koitharu.kotatsu.bookmarks.domain.Bookmark
import org.koitharu.kotatsu.bookmarks.ui.BookmarksAdapter import org.koitharu.kotatsu.bookmarks.ui.BookmarksAdapter
import org.koitharu.kotatsu.core.model.MangaHistory import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.databinding.FragmentDetailsBinding import org.koitharu.kotatsu.databinding.FragmentDetailsBinding
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
import org.koitharu.kotatsu.details.ui.scrobbling.ScrobblingInfoBottomSheet import org.koitharu.kotatsu.details.ui.scrobbling.ScrobblingInfoBottomSheet
import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesBottomSheet import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesBottomSheet
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
@@ -82,6 +79,7 @@ class DetailsFragment :
viewModel.bookmarks.observe(viewLifecycleOwner, ::onBookmarksChanged) viewModel.bookmarks.observe(viewLifecycleOwner, ::onBookmarksChanged)
viewModel.scrobblingInfo.observe(viewLifecycleOwner, ::onScrobblingInfoChanged) viewModel.scrobblingInfo.observe(viewLifecycleOwner, ::onScrobblingInfoChanged)
viewModel.description.observe(viewLifecycleOwner, ::onDescriptionChanged) viewModel.description.observe(viewLifecycleOwner, ::onDescriptionChanged)
viewModel.chapters.observe(viewLifecycleOwner, ::onChaptersChanged)
addMenuProvider(DetailsMenuProvider()) addMenuProvider(DetailsMenuProvider())
} }
@@ -126,18 +124,6 @@ class DetailsFragment :
else -> textViewState.isVisible = false else -> textViewState.isVisible = false
} }
// Info containers
val chapters = manga.chapters
if (chapters.isNullOrEmpty()) {
infoLayout.textViewChapters.isVisible = false
} else {
infoLayout.textViewChapters.isVisible = true
infoLayout.textViewChapters.text = resources.getQuantityString(
R.plurals.chapters,
chapters.size,
chapters.size,
)
}
if (manga.hasRating) { if (manga.hasRating) {
infoLayout.textViewRating.text = String.format("%.1f", manga.rating * 5) infoLayout.textViewRating.text = String.format("%.1f", manga.rating * 5)
infoLayout.ratingContainer.isVisible = true infoLayout.ratingContainer.isVisible = true
@@ -164,14 +150,27 @@ class DetailsFragment :
infoLayout.textViewNsfw.isVisible = manga.isNsfw infoLayout.textViewNsfw.isVisible = manga.isNsfw
// Buttons
buttonRead.isEnabled = !manga.chapters.isNullOrEmpty()
// Chips // Chips
bindTags(manga) bindTags(manga)
} }
} }
private fun onChaptersChanged(chapters: List<ChapterListItem>?) {
val infoLayout = binding.infoLayout
if (chapters.isNullOrEmpty()) {
infoLayout.textViewChapters.isVisible = false
} else {
infoLayout.textViewChapters.isVisible = true
infoLayout.textViewChapters.text = resources.getQuantityString(
R.plurals.chapters,
chapters.size,
chapters.size,
)
}
// Buttons
binding.buttonRead.isEnabled = !chapters.isNullOrEmpty()
}
private fun onDescriptionChanged(description: CharSequence?) { private fun onDescriptionChanged(description: CharSequence?) {
if (description.isNullOrBlank()) { if (description.isNullOrBlank()) {
binding.textViewDescription.setText(R.string.no_description) binding.textViewDescription.setText(R.string.no_description)
@@ -266,7 +265,7 @@ class DetailsFragment :
context = context ?: return, context = context ?: return,
manga = manga, manga = manga,
branch = viewModel.selectedBranchValue, branch = viewModel.selectedBranchValue,
) ),
) )
} }
} }
@@ -276,14 +275,14 @@ class DetailsFragment :
context = v.context, context = v.context,
source = manga.source, source = manga.source,
query = manga.author ?: return, query = manga.author ?: return,
) ),
) )
} }
R.id.imageView_cover -> { R.id.imageView_cover -> {
val options = ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.width, v.height) val options = ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.width, v.height)
startActivity( startActivity(
ImageActivity.newIntent(v.context, manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }), ImageActivity.newIntent(v.context, manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }),
options.toBundle() options.toBundle(),
) )
} }
} }
@@ -309,8 +308,8 @@ class DetailsFragment :
c.chapter.branch == branch c.chapter.branch == branch
}?.let { c -> }?.let { c ->
ReaderState(c.chapter.id, 0, 0) ReaderState(c.chapter.id, 0, 0)
} },
) ),
) )
true true
} }
@@ -343,7 +342,7 @@ class DetailsFragment :
icon = 0, icon = 0,
data = tag, data = tag,
) )
} },
) )
} }

View File

@@ -1,11 +1,11 @@
package org.koitharu.kotatsu.details.ui package org.koitharu.kotatsu.details.ui
import androidx.core.os.LocaleListCompat
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import org.koitharu.kotatsu.base.domain.MangaDataRepository import org.koitharu.kotatsu.base.domain.MangaDataRepository
import org.koitharu.kotatsu.base.domain.MangaIntent import org.koitharu.kotatsu.base.domain.MangaIntent
import org.koitharu.kotatsu.core.model.MangaHistory import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.core.model.getPreferredBranch
import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.details.ui.model.ChapterListItem import org.koitharu.kotatsu.details.ui.model.ChapterListItem
@@ -17,8 +17,6 @@ import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.mapToSet import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.toTitleCase
import org.koitharu.kotatsu.utils.ext.iterator
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
class MangaDetailsDelegate( class MangaDetailsDelegate(
@@ -45,12 +43,7 @@ class MangaDetailsDelegate(
manga = MangaRepository(manga.source).getDetails(manga) manga = MangaRepository(manga.source).getDetails(manga)
// find default branch // find default branch
val hist = historyRepository.getOne(manga) val hist = historyRepository.getOne(manga)
selectedBranch.value = if (hist != null) { selectedBranch.value = manga.getPreferredBranch(hist)
val currentChapter = manga.chapters?.find { it.id == hist.chapterId }
if (currentChapter != null) currentChapter.branch else predictBranch(manga.chapters)
} else {
predictBranch(manga.chapters)
}
mangaData.value = manga mangaData.value = manga
relatedManga.value = runCatching { relatedManga.value = runCatching {
if (manga.source == MangaSource.LOCAL) { if (manga.source == MangaSource.LOCAL) {
@@ -163,22 +156,4 @@ class MangaDetailsDelegate(
} }
return result return result
} }
private fun predictBranch(chapters: List<MangaChapter>?): String? {
if (chapters.isNullOrEmpty()) {
return null
}
val groups = chapters.groupBy { it.branch }
for (locale in LocaleListCompat.getAdjustedDefault()) {
var language = locale.getDisplayLanguage(locale).toTitleCase(locale)
if (groups.containsKey(language)) {
return language
}
language = locale.getDisplayName(locale).toTitleCase(locale)
if (groups.containsKey(language)) {
return language
}
}
return groups.maxByOrNull { it.value.size }?.key
}
} }

View File

@@ -14,7 +14,7 @@ val trackerModule
factory { TrackingRepository(get()) } factory { TrackingRepository(get()) }
factory { TrackerNotificationChannels(androidContext(), get()) } factory { TrackerNotificationChannels(androidContext(), get()) }
factory { Tracker(get(), get(), get()) } factory { Tracker(get(), get(), get(), get()) }
viewModel { FeedViewModel(get()) } viewModel { FeedViewModel(get()) }
} }

View File

@@ -1,8 +1,10 @@
package org.koitharu.kotatsu.tracker.domain package org.koitharu.kotatsu.tracker.domain
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import org.koitharu.kotatsu.core.model.getPreferredBranch
import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.history.domain.HistoryRepository
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.tracker.domain.model.MangaTracking import org.koitharu.kotatsu.tracker.domain.model.MangaTracking
import org.koitharu.kotatsu.tracker.domain.model.MangaUpdates import org.koitharu.kotatsu.tracker.domain.model.MangaUpdates
@@ -12,6 +14,7 @@ import org.koitharu.kotatsu.tracker.work.TrackingItem
class Tracker( class Tracker(
private val settings: AppSettings, private val settings: AppSettings,
private val repository: TrackingRepository, private val repository: TrackingRepository,
private val historyRepository: HistoryRepository,
private val channels: TrackerNotificationChannels, private val channels: TrackerNotificationChannels,
) { ) {
@@ -68,7 +71,7 @@ class Tracker(
suspend fun fetchUpdates(track: MangaTracking, commit: Boolean): MangaUpdates { suspend fun fetchUpdates(track: MangaTracking, commit: Boolean): MangaUpdates {
val manga = MangaRepository(track.manga.source).getDetails(track.manga) val manga = MangaRepository(track.manga.source).getDetails(track.manga)
val updates = compare(track, manga) val updates = compare(track, manga, getBranch(manga))
if (commit) { if (commit) {
repository.saveUpdates(updates) repository.saveUpdates(updates)
} }
@@ -78,7 +81,7 @@ class Tracker(
@VisibleForTesting @VisibleForTesting
suspend fun checkUpdates(manga: Manga, commit: Boolean): MangaUpdates { suspend fun checkUpdates(manga: Manga, commit: Boolean): MangaUpdates {
val track = repository.getTrack(manga) val track = repository.getTrack(manga)
val updates = compare(track, manga) val updates = compare(track, manga, getBranch(manga))
if (commit) { if (commit) {
repository.saveUpdates(updates) repository.saveUpdates(updates)
} }
@@ -90,25 +93,30 @@ class Tracker(
repository.deleteTrack(mangaId) repository.deleteTrack(mangaId)
} }
private suspend fun getBranch(manga: Manga): String? {
val history = historyRepository.getOne(manga)
return manga.getPreferredBranch(history)
}
/** /**
* The main functionality of tracker: check new chapters in [manga] comparing to the [track] * The main functionality of tracker: check new chapters in [manga] comparing to the [track]
*/ */
private fun compare(track: MangaTracking, manga: Manga): MangaUpdates { private fun compare(track: MangaTracking, manga: Manga, branch: String?): MangaUpdates {
if (track.isEmpty()) { if (track.isEmpty()) {
// first check or manga was empty on last check // first check or manga was empty on last check
return MangaUpdates(manga, emptyList(), isValid = false) return MangaUpdates(manga, emptyList(), isValid = false)
} }
val chapters = requireNotNull(manga.chapters) val chapters = requireNotNull(manga.getChapters(branch))
val newChapters = chapters.takeLastWhile { x -> x.id != track.lastChapterId } val newChapters = chapters.takeLastWhile { x -> x.id != track.lastChapterId }
return when { return when {
newChapters.isEmpty() -> { newChapters.isEmpty() -> {
return MangaUpdates(manga, emptyList(), isValid = chapters.lastOrNull()?.id == track.lastChapterId) MangaUpdates(manga, emptyList(), isValid = chapters.lastOrNull()?.id == track.lastChapterId)
} }
newChapters.size == chapters.size -> { newChapters.size == chapters.size -> {
return MangaUpdates(manga, emptyList(), isValid = false) MangaUpdates(manga, emptyList(), isValid = false)
} }
else -> { else -> {
return MangaUpdates(manga, newChapters, isValid = true) MangaUpdates(manga, newChapters, isValid = true)
} }
} }
} }

View File

@@ -18,6 +18,7 @@ import org.koitharu.kotatsu.details.ui.DetailsActivity
import org.koitharu.kotatsu.list.ui.adapter.MangaListListener import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.main.ui.AppBarOwner import org.koitharu.kotatsu.main.ui.AppBarOwner
import org.koitharu.kotatsu.main.ui.MainActivity
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.tracker.ui.adapter.FeedAdapter import org.koitharu.kotatsu.tracker.ui.adapter.FeedAdapter
@@ -25,6 +26,7 @@ import org.koitharu.kotatsu.tracker.work.TrackWorker
import org.koitharu.kotatsu.utils.ext.addMenuProvider import org.koitharu.kotatsu.utils.ext.addMenuProvider
import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.measureHeight import org.koitharu.kotatsu.utils.ext.measureHeight
import org.koitharu.kotatsu.utils.ext.resolveDp
class FeedFragment : class FeedFragment :
BaseFragment<FragmentFeedBinding>(), BaseFragment<FragmentFeedBinding>(),
@@ -39,7 +41,7 @@ class FeedFragment :
override fun onInflateView( override fun onInflateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup? container: ViewGroup?,
) = FragmentFeedBinding.inflate(inflater, container, false) ) = FragmentFeedBinding.inflate(inflater, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -54,7 +56,7 @@ class FeedFragment :
paddingVertical = resources.getDimensionPixelOffset(R.dimen.grid_spacing_outer) paddingVertical = resources.getDimensionPixelOffset(R.dimen.grid_spacing_outer)
val decoration = TypedSpacingItemDecoration( val decoration = TypedSpacingItemDecoration(
FeedAdapter.ITEM_TYPE_FEED to 0, FeedAdapter.ITEM_TYPE_FEED to 0,
fallbackSpacing = spacing fallbackSpacing = spacing,
) )
addItemDecoration(decoration) addItemDecoration(decoration)
} }
@@ -77,12 +79,25 @@ class FeedFragment :
override fun onWindowInsetsChanged(insets: Insets) { override fun onWindowInsetsChanged(insets: Insets) {
val headerHeight = (activity as? AppBarOwner)?.appBar?.measureHeight() ?: insets.top val headerHeight = (activity as? AppBarOwner)?.appBar?.measureHeight() ?: insets.top
binding.recyclerView.updatePadding( binding.root.updatePadding(
top = headerHeight + paddingVertical, left = insets.left,
left = insets.left + paddingHorizontal, right = insets.right,
right = insets.right + paddingHorizontal,
bottom = insets.bottom + paddingVertical,
) )
if (activity is MainActivity) {
binding.recyclerView.updatePadding(
top = headerHeight,
bottom = insets.bottom,
)
binding.swipeRefreshLayout.setProgressViewOffset(
true,
headerHeight + resources.resolveDp(-72),
headerHeight + resources.resolveDp(10),
)
} else {
binding.recyclerView.updatePadding(
bottom = insets.bottom,
)
}
} }
override fun onRetryClick(error: Throwable) = Unit override fun onRetryClick(error: Throwable) = Unit
@@ -101,7 +116,7 @@ class FeedFragment :
Snackbar.make( Snackbar.make(
binding.recyclerView, binding.recyclerView,
R.string.updates_feed_cleared, R.string.updates_feed_cleared,
Snackbar.LENGTH_LONG Snackbar.LENGTH_LONG,
).show() ).show()
} }
@@ -109,7 +124,7 @@ class FeedFragment :
Snackbar.make( Snackbar.make(
binding.recyclerView, binding.recyclerView,
e.getDisplayMessage(resources), e.getDisplayMessage(resources),
Snackbar.LENGTH_SHORT Snackbar.LENGTH_SHORT,
).show() ).show()
} }

View File

@@ -16,12 +16,13 @@ import org.koitharu.kotatsu.list.ui.model.*
import org.koitharu.kotatsu.tracker.domain.TrackingRepository import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import org.koitharu.kotatsu.tracker.domain.model.TrackingLogItem import org.koitharu.kotatsu.tracker.domain.model.TrackingLogItem
import org.koitharu.kotatsu.tracker.ui.model.toFeedItem import org.koitharu.kotatsu.tracker.ui.model.toFeedItem
import org.koitharu.kotatsu.tracker.work.TrackWorker
import org.koitharu.kotatsu.utils.SingleLiveEvent import org.koitharu.kotatsu.utils.SingleLiveEvent
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
import org.koitharu.kotatsu.utils.ext.daysDiff import org.koitharu.kotatsu.utils.ext.daysDiff
class FeedViewModel( class FeedViewModel(
private val repository: TrackingRepository private val repository: TrackingRepository,
) : BaseViewModel() { ) : BaseViewModel() {
private val logList = MutableStateFlow<List<TrackingLogItem>?>(null) private val logList = MutableStateFlow<List<TrackingLogItem>?>(null)
@@ -32,7 +33,7 @@ class FeedViewModel(
val onFeedCleared = SingleLiveEvent<Unit>() val onFeedCleared = SingleLiveEvent<Unit>()
val content = combine( val content = combine(
logList.filterNotNull(), logList.filterNotNull(),
hasNextPage hasNextPage,
) { list, isHasNextPage -> ) { list, isHasNextPage ->
buildList(list.size + 2) { buildList(list.size + 2) {
if (list.isEmpty()) { if (list.isEmpty()) {
@@ -43,7 +44,7 @@ class FeedViewModel(
textPrimary = R.string.text_empty_holder_primary, textPrimary = R.string.text_empty_holder_primary,
textSecondary = R.string.text_feed_holder, textSecondary = R.string.text_feed_holder,
actionStringRes = 0, actionStringRes = 0,
) ),
) )
} else { } else {
list.mapListTo(this) list.mapListTo(this)
@@ -54,7 +55,7 @@ class FeedViewModel(
} }
}.asLiveDataDistinct( }.asLiveDataDistinct(
viewModelScope.coroutineContext + Dispatchers.Default, viewModelScope.coroutineContext + Dispatchers.Default,
listOf(header, LoadingState) listOf(header, LoadingState),
) )
init { init {

View File

@@ -29,6 +29,9 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:background="@null" android:background="@null"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingHorizontal="@dimen/margin_normal"
app:tabGravity="center" app:tabGravity="center"
app:tabMode="scrollable" /> app:tabMode="scrollable" />