Improve updated manga screen
This commit is contained in:
@@ -172,6 +172,14 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
get() = prefs.getBoolean(KEY_HISTORY_GROUPING, true)
|
||||
set(value) = prefs.edit { putBoolean(KEY_HISTORY_GROUPING, value) }
|
||||
|
||||
var isUpdatedGroupingEnabled: Boolean
|
||||
get() = prefs.getBoolean(KEY_UPDATED_GROUPING, true)
|
||||
set(value) = prefs.edit { putBoolean(KEY_UPDATED_GROUPING, value) }
|
||||
|
||||
var isFeedHeaderVisible: Boolean
|
||||
get() = prefs.getBoolean(KEY_FEED_HEADER, true)
|
||||
set(value) = prefs.edit { putBoolean(KEY_FEED_HEADER, value) }
|
||||
|
||||
val isReadingIndicatorsEnabled: Boolean
|
||||
get() = prefs.getBoolean(KEY_READING_INDICATORS, true)
|
||||
|
||||
@@ -575,6 +583,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
const val KEY_BACKUP_PERIODICAL_OUTPUT = "backup_periodic_output"
|
||||
const val KEY_BACKUP_PERIODICAL_LAST = "backup_periodic_last"
|
||||
const val KEY_HISTORY_GROUPING = "history_grouping"
|
||||
const val KEY_UPDATED_GROUPING = "updated_grouping"
|
||||
const val KEY_READING_INDICATORS = "reading_indicators"
|
||||
const val KEY_REVERSE_CHAPTERS = "reverse_chapters"
|
||||
const val KEY_GRID_VIEW_CHAPTERS = "grid_view_chapters"
|
||||
@@ -652,5 +661,6 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
const val KEY_STATS_ENABLED = "stats_on"
|
||||
const val KEY_APP_UPDATE = "app_update"
|
||||
const val KEY_APP_TRANSLATION = "about_app_translation"
|
||||
const val KEY_FEED_HEADER = "feed_header"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ import org.xmlpull.v1.XmlPullParser
|
||||
import org.xmlpull.v1.XmlPullParserException
|
||||
import java.io.File
|
||||
import kotlin.math.roundToLong
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
val Context.activityManager: ActivityManager?
|
||||
get() = getSystemService(ACTIVITY_SERVICE) as? ActivityManager
|
||||
@@ -141,7 +142,7 @@ fun Window.setNavigationBarTransparentCompat(context: Context, elevation: Float,
|
||||
} else {
|
||||
// Set navbar scrim 70% of navigationBarColor
|
||||
ElevationOverlayProvider(context).compositeOverlayIfNeeded(
|
||||
context.getThemeColor(com.google.android.material.R.attr.colorSurfaceContainer, alphaFactor),
|
||||
context.getThemeColor(materialR.attr.colorSurfaceContainer, alphaFactor),
|
||||
elevation,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.koitharu.kotatsu.history.ui.HistoryListFragment
|
||||
import org.koitharu.kotatsu.list.ui.config.ListConfigBottomSheet
|
||||
import org.koitharu.kotatsu.list.ui.config.ListConfigSection
|
||||
import org.koitharu.kotatsu.suggestions.ui.SuggestionsFragment
|
||||
import org.koitharu.kotatsu.tracker.ui.updates.UpdatesFragment
|
||||
|
||||
class MangaListMenuProvider(
|
||||
private val fragment: Fragment,
|
||||
@@ -26,6 +27,7 @@ class MangaListMenuProvider(
|
||||
is HistoryListFragment -> ListConfigSection.History
|
||||
is SuggestionsFragment -> ListConfigSection.Suggestions
|
||||
is FavouritesListFragment -> ListConfigSection.Favorites(fragment.categoryId)
|
||||
is UpdatesFragment -> ListConfigSection.Updated
|
||||
else -> ListConfigSection.General
|
||||
}
|
||||
ListConfigBottomSheet.show(fragment.childFragmentManager, section)
|
||||
|
||||
@@ -14,7 +14,6 @@ import com.google.android.material.button.MaterialButtonToggleGroup
|
||||
import com.google.android.material.slider.Slider
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
|
||||
import org.koitharu.kotatsu.core.util.ext.setValueRounded
|
||||
@@ -22,7 +21,6 @@ import org.koitharu.kotatsu.core.util.ext.showDistinct
|
||||
import org.koitharu.kotatsu.core.util.ext.withArgs
|
||||
import org.koitharu.kotatsu.core.util.progress.IntPercentLabelFormatter
|
||||
import org.koitharu.kotatsu.databinding.SheetListModeBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ListConfigBottomSheet :
|
||||
@@ -31,10 +29,6 @@ class ListConfigBottomSheet :
|
||||
MaterialButtonToggleGroup.OnButtonCheckedListener, CompoundButton.OnCheckedChangeListener,
|
||||
AdapterView.OnItemSelectedListener {
|
||||
|
||||
@Inject
|
||||
@Deprecated("")
|
||||
lateinit var settings: AppSettings
|
||||
|
||||
private val viewModel by viewModels<ListConfigViewModel>()
|
||||
|
||||
override fun onCreateViewBinding(
|
||||
@@ -57,11 +51,11 @@ class ListConfigBottomSheet :
|
||||
|
||||
binding.checkableGroup.addOnButtonCheckedListener(this)
|
||||
|
||||
binding.switchGrouping.isVisible = viewModel.isGroupingAvailable
|
||||
if (viewModel.isGroupingAvailable) {
|
||||
binding.switchGrouping.isEnabled = settings.historySortOrder.isGroupingSupported()
|
||||
binding.switchGrouping.isVisible = viewModel.isGroupingSupported
|
||||
if (viewModel.isGroupingSupported) {
|
||||
binding.switchGrouping.isEnabled = viewModel.isGroupingAvailable
|
||||
}
|
||||
binding.switchGrouping.isChecked = settings.isHistoryGroupingEnabled
|
||||
binding.switchGrouping.isChecked = viewModel.isGroupingEnabled
|
||||
binding.switchGrouping.setOnCheckedChangeListener(this)
|
||||
|
||||
val sortOrders = viewModel.getSortOrders()
|
||||
@@ -99,7 +93,7 @@ class ListConfigBottomSheet :
|
||||
|
||||
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
|
||||
when (buttonView.id) {
|
||||
R.id.switch_grouping -> settings.isHistoryGroupingEnabled = isChecked
|
||||
R.id.switch_grouping -> viewModel.isGroupingEnabled = isChecked
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +107,7 @@ class ListConfigBottomSheet :
|
||||
when (parent.id) {
|
||||
R.id.spinner_order -> {
|
||||
viewModel.setSortOrder(position)
|
||||
viewBinding?.switchGrouping?.isEnabled = settings.historySortOrder.isGroupingSupported()
|
||||
viewBinding?.switchGrouping?.isEnabled = viewModel.isGroupingAvailable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,4 +18,7 @@ sealed interface ListConfigSection : Parcelable {
|
||||
|
||||
@Parcelize
|
||||
data object Suggestions : ListConfigSection
|
||||
|
||||
@Parcelize
|
||||
data object Updated : ListConfigSection
|
||||
}
|
||||
|
||||
@@ -26,16 +26,18 @@ class ListConfigViewModel @Inject constructor(
|
||||
var listMode: ListMode
|
||||
get() = when (section) {
|
||||
is ListConfigSection.Favorites -> settings.favoritesListMode
|
||||
ListConfigSection.General -> settings.listMode
|
||||
ListConfigSection.History -> settings.historyListMode
|
||||
ListConfigSection.Suggestions -> settings.suggestionsListMode
|
||||
ListConfigSection.General,
|
||||
ListConfigSection.Updated -> settings.listMode
|
||||
}
|
||||
set(value) {
|
||||
when (section) {
|
||||
is ListConfigSection.Favorites -> settings.favoritesListMode = value
|
||||
ListConfigSection.General -> settings.listMode = value
|
||||
ListConfigSection.History -> settings.historyListMode = value
|
||||
ListConfigSection.Suggestions -> settings.suggestionsListMode = value
|
||||
ListConfigSection.Updated,
|
||||
ListConfigSection.General -> settings.listMode = value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,19 +47,40 @@ class ListConfigViewModel @Inject constructor(
|
||||
settings.gridSize = value
|
||||
}
|
||||
|
||||
val isGroupingSupported: Boolean
|
||||
get() = section == ListConfigSection.History || section == ListConfigSection.Updated
|
||||
|
||||
val isGroupingAvailable: Boolean
|
||||
get() = section == ListConfigSection.History
|
||||
get() = when (section) {
|
||||
ListConfigSection.History -> settings.historySortOrder.isGroupingSupported()
|
||||
ListConfigSection.Updated -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
var isGroupingEnabled: Boolean
|
||||
get() = when (section) {
|
||||
ListConfigSection.History -> settings.isHistoryGroupingEnabled
|
||||
ListConfigSection.Updated -> settings.isUpdatedGroupingEnabled
|
||||
else -> false
|
||||
}
|
||||
set(value) = when (section) {
|
||||
ListConfigSection.History -> settings.isHistoryGroupingEnabled = value
|
||||
ListConfigSection.Updated -> settings.isUpdatedGroupingEnabled = value
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
fun getSortOrders(): List<ListSortOrder>? = when (section) {
|
||||
is ListConfigSection.Favorites -> ListSortOrder.FAVORITES
|
||||
ListConfigSection.General -> null
|
||||
ListConfigSection.History -> ListSortOrder.HISTORY
|
||||
ListConfigSection.Suggestions -> ListSortOrder.SUGGESTIONS
|
||||
ListConfigSection.Updated -> null
|
||||
}?.sortedByOrdinal()
|
||||
|
||||
fun getSelectedSortOrder(): ListSortOrder? = when (section) {
|
||||
is ListConfigSection.Favorites -> getCategorySortOrder(section.categoryId)
|
||||
ListConfigSection.General -> null
|
||||
ListConfigSection.Updated -> null
|
||||
ListConfigSection.History -> settings.historySortOrder
|
||||
ListConfigSection.Suggestions -> ListSortOrder.RELEVANCE // TODO
|
||||
}
|
||||
@@ -77,6 +100,7 @@ class ListConfigViewModel @Inject constructor(
|
||||
ListConfigSection.History -> settings.historySortOrder = value
|
||||
|
||||
ListConfigSection.Suggestions -> Unit
|
||||
ListConfigSection.Updated -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.koitharu.kotatsu.tracker.data
|
||||
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Junction
|
||||
import androidx.room.Relation
|
||||
import org.koitharu.kotatsu.core.db.entity.MangaEntity
|
||||
import org.koitharu.kotatsu.core.db.entity.MangaTagsEntity
|
||||
import org.koitharu.kotatsu.core.db.entity.TagEntity
|
||||
|
||||
class MangaWithTrack(
|
||||
@Embedded val track: TrackEntity,
|
||||
@Relation(
|
||||
parentColumn = "manga_id",
|
||||
entityColumn = "manga_id",
|
||||
)
|
||||
val manga: MangaEntity,
|
||||
@Relation(
|
||||
parentColumn = "manga_id",
|
||||
entityColumn = "tag_id",
|
||||
associateBy = Junction(MangaTagsEntity::class),
|
||||
)
|
||||
val tags: List<TagEntity>,
|
||||
)
|
||||
@@ -6,7 +6,6 @@ import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Upsert
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.koitharu.kotatsu.core.db.entity.MangaWithTags
|
||||
|
||||
@Dao
|
||||
abstract class TracksDao {
|
||||
@@ -47,12 +46,12 @@ abstract class TracksDao {
|
||||
abstract fun observeNewChapters(mangaId: Long): Flow<Int?>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT manga.* FROM tracks LEFT JOIN manga ON manga.manga_id = tracks.manga_id WHERE chapters_new > 0 ORDER BY last_chapter_date DESC")
|
||||
abstract fun observeUpdatedManga(): Flow<List<MangaWithTags>>
|
||||
@Query("SELECT * FROM tracks WHERE chapters_new > 0 ORDER BY last_chapter_date DESC")
|
||||
abstract fun observeUpdatedManga(): Flow<List<MangaWithTrack>>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT manga.* FROM tracks LEFT JOIN manga ON manga.manga_id = tracks.manga_id WHERE chapters_new > 0 ORDER BY last_chapter_date DESC LIMIT :limit")
|
||||
abstract fun observeUpdatedManga(limit: Int): Flow<List<MangaWithTags>>
|
||||
@Query("SELECT * FROM tracks WHERE chapters_new > 0 ORDER BY last_chapter_date DESC LIMIT :limit")
|
||||
abstract fun observeUpdatedManga(limit: Int): Flow<List<MangaWithTrack>>
|
||||
|
||||
@Query("DELETE FROM tracks")
|
||||
abstract suspend fun clear()
|
||||
|
||||
@@ -12,6 +12,7 @@ import kotlinx.coroutines.flow.onStart
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.core.db.entity.MangaEntity
|
||||
import org.koitharu.kotatsu.core.db.entity.toManga
|
||||
import org.koitharu.kotatsu.core.db.entity.toMangaTags
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.util.ext.ifZero
|
||||
@@ -59,13 +60,20 @@ class TrackingRepository @Inject constructor(
|
||||
.onStart { gcIfNotCalled() }
|
||||
}
|
||||
|
||||
fun observeUpdatedManga(limit: Int = 0): Flow<List<Manga>> {
|
||||
fun observeUpdatedManga(limit: Int = 0): Flow<List<MangaTracking>> {
|
||||
return if (limit == 0) {
|
||||
db.getTracksDao().observeUpdatedManga()
|
||||
} else {
|
||||
db.getTracksDao().observeUpdatedManga(limit)
|
||||
}.mapItems { it.toManga() }
|
||||
.distinctUntilChanged()
|
||||
}.mapItems {
|
||||
MangaTracking(
|
||||
manga = it.manga.toManga(it.tags.toMangaTags()),
|
||||
lastChapterId = it.track.lastChapterId,
|
||||
lastCheck = it.track.lastCheckTime.toInstantOrNull(),
|
||||
lastChapterDate = it.track.lastChapterDate.toInstantOrNull(),
|
||||
newChapters = it.track.newChapters,
|
||||
)
|
||||
}.distinctUntilChanged()
|
||||
.onStart { gcIfNotCalled() }
|
||||
}
|
||||
|
||||
@@ -79,6 +87,8 @@ class TrackingRepository @Inject constructor(
|
||||
manga = it.manga.toManga(emptySet()),
|
||||
lastChapterId = it.track.lastChapterId,
|
||||
lastCheck = it.track.lastCheckTime.toInstantOrNull(),
|
||||
lastChapterDate = it.track.lastChapterDate.toInstantOrNull(),
|
||||
newChapters = it.track.newChapters,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -90,6 +100,8 @@ class TrackingRepository @Inject constructor(
|
||||
manga = manga,
|
||||
lastChapterId = track?.lastChapterId ?: NO_ID,
|
||||
lastCheck = track?.lastCheckTime?.toInstantOrNull(),
|
||||
lastChapterDate = track?.lastChapterDate?.toInstantOrNull(),
|
||||
newChapters = track?.newChapters ?: 0,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,10 @@ data class MangaTracking(
|
||||
val manga: Manga,
|
||||
val lastChapterId: Long,
|
||||
val lastCheck: Instant?,
|
||||
val lastChapterDate: Instant?,
|
||||
val newChapters: Int,
|
||||
) {
|
||||
|
||||
fun isEmpty(): Boolean {
|
||||
return lastChapterId == 0L
|
||||
}
|
||||
|
||||
@@ -11,11 +11,15 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import coil.ImageLoader
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver
|
||||
import org.koitharu.kotatsu.core.ui.BaseFragment
|
||||
import org.koitharu.kotatsu.core.ui.list.PaginationScrollListener
|
||||
import org.koitharu.kotatsu.core.ui.list.RecyclerScrollKeeper
|
||||
import org.koitharu.kotatsu.core.ui.util.MenuInvalidator
|
||||
import org.koitharu.kotatsu.core.ui.widgets.TipView
|
||||
import org.koitharu.kotatsu.core.util.RecyclerViewScrollCallback
|
||||
import org.koitharu.kotatsu.core.util.ext.addMenuProvider
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||
@@ -60,20 +64,15 @@ class FeedFragment :
|
||||
setHasFixedSize(true)
|
||||
addOnScrollListener(PaginationScrollListener(4, this@FeedFragment))
|
||||
addItemDecoration(TypedListSpacingDecoration(context, true))
|
||||
RecyclerScrollKeeper(this).attach()
|
||||
}
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(this)
|
||||
addMenuProvider(
|
||||
FeedMenuProvider(
|
||||
binding.recyclerView,
|
||||
viewModel,
|
||||
),
|
||||
)
|
||||
addMenuProvider(FeedMenuProvider(binding.recyclerView, viewModel))
|
||||
|
||||
viewModel.isHeaderEnabled.drop(1).observe(viewLifecycleOwner, MenuInvalidator(requireActivity()))
|
||||
viewModel.content.observe(viewLifecycleOwner, this::onListChanged)
|
||||
viewModel.onError.observeEvent(viewLifecycleOwner, SnackbarErrorObserver(binding.recyclerView, this))
|
||||
viewModel.onFeedCleared.observeEvent(viewLifecycleOwner) {
|
||||
onFeedCleared()
|
||||
}
|
||||
viewModel.onFeedCleared.observeEvent(viewLifecycleOwner) { onFeedCleared() }
|
||||
viewModel.isRunning.observe(viewLifecycleOwner, this::onIsTrackerRunningChanged)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,12 +22,22 @@ class FeedMenuProvider(
|
||||
menuInflater.inflate(R.menu.opt_feed, menu)
|
||||
}
|
||||
|
||||
override fun onPrepareMenu(menu: Menu) {
|
||||
super.onPrepareMenu(menu)
|
||||
menu.findItem(R.id.action_show_updated)?.isChecked = viewModel.isHeaderEnabled.value
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
|
||||
R.id.action_update -> {
|
||||
viewModel.update()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_show_updated -> {
|
||||
viewModel.setHeaderEnabled(!menuItem.isChecked)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_clear_feed -> {
|
||||
CheckBoxAlertDialog.Builder(context)
|
||||
.setTitle(R.string.clear_updates_feed)
|
||||
|
||||
@@ -6,22 +6,25 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsStateFlow
|
||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.core.ui.model.DateTimeAgo
|
||||
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
||||
import org.koitharu.kotatsu.core.util.ext.call
|
||||
import org.koitharu.kotatsu.core.util.ext.calculateTimeAgo
|
||||
import org.koitharu.kotatsu.core.util.ext.call
|
||||
import org.koitharu.kotatsu.list.domain.ListExtraProvider
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||
import org.koitharu.kotatsu.list.ui.model.ListHeader
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
import org.koitharu.kotatsu.list.ui.model.toUi
|
||||
import org.koitharu.kotatsu.list.ui.model.toGridModel
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.tracker.domain.model.TrackingLogItem
|
||||
import org.koitharu.kotatsu.tracker.ui.feed.model.UpdatedMangaHeader
|
||||
@@ -34,6 +37,7 @@ private const val PAGE_SIZE = 20
|
||||
|
||||
@HiltViewModel
|
||||
class FeedViewModel @Inject constructor(
|
||||
private val settings: AppSettings,
|
||||
private val repository: TrackingRepository,
|
||||
private val scheduler: TrackWorker.Scheduler,
|
||||
private val listExtraProvider: ListExtraProvider,
|
||||
@@ -45,6 +49,12 @@ class FeedViewModel @Inject constructor(
|
||||
val isRunning = scheduler.observeIsRunning()
|
||||
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, false)
|
||||
|
||||
val isHeaderEnabled = settings.observeAsStateFlow(
|
||||
scope = viewModelScope + Dispatchers.Default,
|
||||
key = AppSettings.KEY_FEED_HEADER,
|
||||
valueProducer = { isFeedHeaderVisible },
|
||||
)
|
||||
|
||||
val onFeedCleared = MutableEventFlow<Unit>()
|
||||
val content = combine(
|
||||
observeHeader(),
|
||||
@@ -94,6 +104,10 @@ class FeedViewModel @Inject constructor(
|
||||
scheduler.startNow()
|
||||
}
|
||||
|
||||
fun setHeaderEnabled(value: Boolean) {
|
||||
settings.isFeedHeaderVisible = value
|
||||
}
|
||||
|
||||
private fun List<TrackingLogItem>.mapListTo(destination: MutableList<ListModel>) {
|
||||
var prevDate: DateTimeAgo? = null
|
||||
for (item in this) {
|
||||
@@ -106,11 +120,19 @@ class FeedViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeHeader() = repository.observeUpdatedManga(10).map { mangaList ->
|
||||
if (mangaList.isEmpty()) {
|
||||
null
|
||||
private fun observeHeader() = isHeaderEnabled.flatMapLatest { hasHeader ->
|
||||
if (hasHeader) {
|
||||
repository.observeUpdatedManga(10).map { mangaList ->
|
||||
if (mangaList.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
UpdatedMangaHeader(
|
||||
mangaList.map { it.manga.toGridModel(listExtraProvider) },
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
UpdatedMangaHeader(mangaList.toUi(ListMode.GRID, listExtraProvider))
|
||||
flowOf(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,15 +11,24 @@ import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
||||
import org.koitharu.kotatsu.core.ui.model.DateTimeAgo
|
||||
import org.koitharu.kotatsu.core.util.ext.calculateTimeAgo
|
||||
import org.koitharu.kotatsu.core.util.ext.onFirst
|
||||
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
|
||||
import org.koitharu.kotatsu.list.domain.ListExtraProvider
|
||||
import org.koitharu.kotatsu.list.ui.MangaListViewModel
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||
import org.koitharu.kotatsu.list.ui.model.ListHeader
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
import org.koitharu.kotatsu.list.ui.model.toErrorState
|
||||
import org.koitharu.kotatsu.list.ui.model.toUi
|
||||
import org.koitharu.kotatsu.list.ui.model.toGridModel
|
||||
import org.koitharu.kotatsu.list.ui.model.toListDetailedModel
|
||||
import org.koitharu.kotatsu.list.ui.model.toListModel
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.tracker.domain.model.MangaTracking
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
@@ -32,8 +41,9 @@ class UpdatesViewModel @Inject constructor(
|
||||
|
||||
override val content = combine(
|
||||
repository.observeUpdatedManga(),
|
||||
settings.observeAsFlow(AppSettings.KEY_UPDATED_GROUPING) { isUpdatedGroupingEnabled },
|
||||
listMode,
|
||||
) { mangaList, mode ->
|
||||
) { mangaList, grouping, mode ->
|
||||
when {
|
||||
mangaList.isEmpty() -> listOf(
|
||||
EmptyState(
|
||||
@@ -44,7 +54,7 @@ class UpdatesViewModel @Inject constructor(
|
||||
),
|
||||
)
|
||||
|
||||
else -> mangaList.toUi(mode, extraProvider)
|
||||
else -> mangaList.toUi(mode, grouping)
|
||||
}
|
||||
}.onStart {
|
||||
loadingCounter.increment()
|
||||
@@ -69,4 +79,26 @@ class UpdatesViewModel @Inject constructor(
|
||||
repository.clearUpdates(ids)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun List<MangaTracking>.toUi(mode: ListMode, grouped: Boolean): List<ListModel> {
|
||||
val result = ArrayList<ListModel>(if (grouped) (size * 1.4).toInt() else size)
|
||||
var prevHeader: DateTimeAgo? = null
|
||||
for (item in this) {
|
||||
if (grouped) {
|
||||
val header = item.lastChapterDate?.let { calculateTimeAgo(it) }
|
||||
if (header != prevHeader) {
|
||||
if (header != null) {
|
||||
result += ListHeader(header)
|
||||
}
|
||||
prevHeader = header
|
||||
}
|
||||
}
|
||||
result += when (mode) {
|
||||
ListMode.LIST -> item.manga.toListModel(extraProvider)
|
||||
ListMode.DETAILED_LIST -> item.manga.toListDetailedModel(extraProvider)
|
||||
ListMode.GRID -> item.manga.toGridModel(extraProvider)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,13 @@
|
||||
android:title="@string/update"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_show_updated"
|
||||
android:checkable="true"
|
||||
android:orderInCategory="50"
|
||||
android:title="@string/show_updated"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_clear_feed"
|
||||
android:orderInCategory="50"
|
||||
|
||||
@@ -652,4 +652,5 @@
|
||||
<string name="fix">Fix</string>
|
||||
<string name="missing_storage_permission">There is no permission to access manga on external storage</string>
|
||||
<string name="last_used">Last used</string>
|
||||
<string name="show_updated">Show updated</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user