Restoring Kotatsu schema backups

master
Zakhar Timoshenko 2 years ago
parent 91a3ddff4e
commit ff021c6eec
Signed by: Xtimms
SSH Key Fingerprint: SHA256:wH6spYepK/A5erBh7ZyAnr1ru9H4eaMVBEuiw6DSpxI

@ -13,12 +13,12 @@
<deviceKey> <deviceKey>
<Key> <Key>
<type value="VIRTUAL_DEVICE_PATH" /> <type value="VIRTUAL_DEVICE_PATH" />
<value value="C:\Users\xtimms\.android\avd\Small_Phone_API_34.avd" /> <value value="C:\Users\xtimms\.android\avd\Pixel_API_27.avd" />
</Key> </Key>
</deviceKey> </deviceKey>
</Target> </Target>
</targetSelectedWithDropDown> </targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2024-03-17T20:36:24.681011800Z" /> <timeTargetWasSelectedWithDropDown value="2024-03-27T16:43:21.742325100Z" />
</State> </State>
</entry> </entry>
</value> </value>

@ -21,124 +21,5 @@ class DatabasePrePopulateCallback(private val resources: Resources) : RoomDataba
0L, 0L,
) )
) )
db.execSQL(
"INSERT INTO favourite_categories (created_at, sort_key, title, `order`, track, show_in_lib, `deleted_at`) VALUES (?,?,?,?,?,?,?)",
arrayOf(
System.currentTimeMillis(),
1,
resources.getString(R.string.reading),
SortOrder.NEWEST.name,
1,
1,
0L,
)
)
db.execSQL(
"INSERT INTO favourite_categories (created_at, sort_key, title, `order`, track, show_in_lib, `deleted_at`) VALUES (?,?,?,?,?,?,?)",
arrayOf(
System.currentTimeMillis(),
1,
resources.getString(R.string.completed),
SortOrder.NEWEST.name,
1,
1,
0L,
)
)
db.execSQL(
"INSERT INTO favourite_categories (created_at, sort_key, title, `order`, track, show_in_lib, `deleted_at`) VALUES (?,?,?,?,?,?,?)",
arrayOf(
System.currentTimeMillis(),
1,
resources.getString(R.string.dropped),
SortOrder.NEWEST.name,
1,
1,
0L,
)
)
db.execSQL(
"INSERT INTO sources (source, enabled, sort_key) VALUES (?,?,?)",
arrayOf(
"MANGADEX",
1,
1,
)
)
db.execSQL(
"INSERT INTO sources (source, enabled, sort_key) VALUES (?,?,?)",
arrayOf(
"DESUME",
1,
1,
)
)
db.execSQL(
"INSERT INTO manga (manga_id, title, alt_title, url, public_url, rating, nsfw, cover_url, large_cover_url, state, author, source) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",
arrayOf(
4427365311541330000,
"Seitokai ni mo Ana wa Aru!",
"",
"822c9883-385c-4fd0-9523-16e7789cbeae",
"https://mangadex.org/title/822c9883-385c-4fd0-9523-16e7789cbeae",
-1.0,
0,
"https://mangadex.org/covers/822c9883-385c-4fd0-9523-16e7789cbeae/f886822a-80c3-484c-ad75-9aa32abedc18.jpg.256.jpg",
"https://mangadex.org/covers/822c9883-385c-4fd0-9523-16e7789cbeae/f886822a-80c3-484c-ad75-9aa32abedc18.jpg",
"FINISHED",
"Muchi Maro",
"MANGADEX",
)
)
db.execSQL(
"INSERT INTO manga (manga_id, title, alt_title, url, public_url, rating, nsfw, cover_url, large_cover_url, state, author, source) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",
arrayOf(
-5513532524243987690,
"Тотальный гарем",
"Shuumatsu no Harem",
"/manga/api/694",
"https://desu.me/manga/z-shuumatsu-no-harem.694/",
1.0,
1,
"https://desu.me/data/manga/covers/preview/694.jpg",
"https://desu.me/data/manga/covers/original/694.jpg",
"ONGOING",
"",
"DESUME",
)
)
db.execSQL(
"INSERT INTO favourites (manga_id, category_id, sort_key, created_at, deleted_at) VALUES (?,?,?,?,?)",
arrayOf(
4427365311541330000,
1,
0,
1705944302882,
0,
)
)
db.execSQL(
"INSERT INTO favourites (manga_id, category_id, sort_key, created_at, deleted_at) VALUES (?,?,?,?,?)",
arrayOf(
-5513532524243987690,
1,
0,
1705944302882,
0,
)
)
db.execSQL(
"INSERT into history (manga_id, created_at, updated_at, chapter_id, page, scroll, percent, deleted_at) VALUES (?,?,?,?,?,?,?,?)",
arrayOf(
-5513532524243987690,
1710617414,
1710617414,
1,
3,
0.3,
0.4,
0
)
)
} }
} }

@ -0,0 +1,18 @@
package org.xtimms.tokusho.core.screens
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@Composable
fun LoadingScreen(modifier: Modifier = Modifier) {
Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
CircularProgressIndicator()
}
}

@ -34,7 +34,6 @@ import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.MenuBook import androidx.compose.material.icons.automirrored.outlined.MenuBook
import androidx.compose.material.icons.outlined.Block import androidx.compose.material.icons.outlined.Block

@ -251,7 +251,7 @@ fun DetailsView(
title = it.chapter.name, title = it.chapter.name,
date = it.chapter.uploadDate, date = it.chapter.uploadDate,
scanlator = it.chapter.scanlator, scanlator = it.chapter.scanlator,
read = it.isUnread, read = !it.isUnread,
bookmark = false, bookmark = false,
selected = false, selected = false,
onLongClick = { /*TODO*/ }, onLongClick = { /*TODO*/ },

@ -48,6 +48,7 @@ import org.xtimms.tokusho.core.components.effects.updateAnimatedItemsState
import org.xtimms.tokusho.core.prefs.AppSettings import org.xtimms.tokusho.core.prefs.AppSettings
import org.xtimms.tokusho.core.prefs.SWIPE_TUTORIAL import org.xtimms.tokusho.core.prefs.SWIPE_TUTORIAL
import org.xtimms.tokusho.core.screens.EmptyScreen import org.xtimms.tokusho.core.screens.EmptyScreen
import org.xtimms.tokusho.core.screens.LoadingScreen
import org.xtimms.tokusho.utils.lang.calculateTimeAgo import org.xtimms.tokusho.utils.lang.calculateTimeAgo
import org.xtimms.tokusho.utils.lang.isSameDay import org.xtimms.tokusho.utils.lang.isSameDay
import java.time.Instant import java.time.Instant
@ -70,11 +71,11 @@ fun HistoryView(
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
var isUserTrySwipe by remember { mutableStateOf(false) } var isUserTrySwipe by remember { mutableStateOf(false) }
val history by viewModel.content.collectAsStateWithLifecycle(emptyList()) val history by viewModel.content.collectAsStateWithLifecycle(null)
DisposableEffect(Unit) { DisposableEffect(Unit) {
onDispose { onDispose {
if (history.isNotEmpty() && isUserTrySwipe) { if (history?.isNotEmpty() == true && isUserTrySwipe) {
AppSettings.updateValue(SWIPE_TUTORIAL, isUserTrySwipe) AppSettings.updateValue(SWIPE_TUTORIAL, isUserTrySwipe)
} }
} }
@ -83,7 +84,7 @@ fun HistoryView(
val animatedList = run { val animatedList = run {
val list = emptyList<RowEntity>().toMutableList() val list = emptyList<RowEntity>().toMutableList()
var readDate: Instant? = null var readDate: Instant? = null
history.forEach { item -> history?.forEach { item ->
if (readDate === null || !isSameDay( if (readDate === null || !isSameDay(
item.history.updatedAt.toEpochMilli(), item.history.updatedAt.toEpochMilli(),
@ -116,114 +117,127 @@ fun HistoryView(
Box( Box(
Modifier.fillMaxSize() Modifier.fillMaxSize()
) { ) {
Column(Modifier.fillMaxSize()) { history.let {
LazyColumn( if (it == null) {
modifier = Modifier LoadingScreen(Modifier.padding(padding))
.collapsable( } else if (it.isEmpty()) {
state = scrollState, EmptyScreen(
topBarHeightPx = topBarHeightPx, icon = Icons.Outlined.History,
topBarOffsetY = topBarOffsetY title = R.string.empty_history_title,
) description = R.string.empty_history_description
.padding(padding) )
) { } else {
animatedItemsIndexed( Column(Modifier.fillMaxSize()) {
state = animatedList.value, LazyColumn(
key = { rowItem -> rowItem.key }, modifier = Modifier
) { index, item -> .collapsable(
when (item.type) { state = scrollState,
RowEntityType.Header -> ListGroupHeader( topBarHeightPx = topBarHeightPx,
calculateTimeAgo(item.day).format( topBarOffsetY = topBarOffsetY
LocalContext.current.resources
) )
) .padding(padding)
RowEntityType.Item -> SwipeActions( ) {
startActionsConfig = SwipeActionsConfig( animatedItemsIndexed(
threshold = 0.33f, state = animatedList.value,
background = MaterialTheme.colorScheme.errorContainer, key = { rowItem -> rowItem.key },
backgroundActive = MaterialTheme.colorScheme.error, ) { index, item ->
iconTint = MaterialTheme.colorScheme.onError, when (item.type) {
icon = Icons.Outlined.DeleteForever, RowEntityType.Header -> ListGroupHeader(
stayDismissed = true, calculateTimeAgo(item.day).format(
onDismiss = { LocalContext.current.resources
viewModel.removeFromHistory(item.historyItemModel!!) )
} )
),
endActionsConfig = SwipeActionsConfig(
threshold = 0.33f,
background = MaterialTheme.colorScheme.tertiaryContainer,
backgroundActive = MaterialTheme.colorScheme.tertiary,
iconTint = MaterialTheme.colorScheme.onTertiary,
icon = Icons.Outlined.PlayArrow,
stayDismissed = false,
onDismiss = {
navigateToReader()
}
),
onTried = { isUserTrySwipe = true },
showTutorial = false,
) { state ->
val size = with(LocalDensity.current) {
java.lang.Float.max(
java.lang.Float.min(
16.dp.toPx(),
abs(state.offset.value)
), 0f
).toDp()
}
val animateCorners by remember { RowEntityType.Item -> SwipeActions(
derivedStateOf { startActionsConfig = SwipeActionsConfig(
state.offset.value.absoluteValue > 30 threshold = 0.33f,
} background = MaterialTheme.colorScheme.errorContainer,
} backgroundActive = MaterialTheme.colorScheme.error,
val startCorners by animateDpAsState( iconTint = MaterialTheme.colorScheme.onError,
targetValue = when { icon = Icons.Outlined.DeleteForever,
state.dismissDirection == DismissDirection.StartToEnd && stayDismissed = true,
animateCorners -> 8.dp onDismiss = {
viewModel.removeFromHistory(item.historyItemModel!!)
}
),
endActionsConfig = SwipeActionsConfig(
threshold = 0.33f,
background = MaterialTheme.colorScheme.tertiaryContainer,
backgroundActive = MaterialTheme.colorScheme.tertiary,
iconTint = MaterialTheme.colorScheme.onTertiary,
icon = Icons.Outlined.PlayArrow,
stayDismissed = false,
onDismiss = {
navigateToReader()
}
),
onTried = { isUserTrySwipe = true },
showTutorial = false,
) { state ->
val size = with(LocalDensity.current) {
java.lang.Float.max(
java.lang.Float.min(
16.dp.toPx(),
abs(state.offset.value)
), 0f
).toDp()
}
else -> 0.dp val animateCorners by remember {
}, label = "startCorners" derivedStateOf {
) state.offset.value.absoluteValue > 30
val endCorners by animateDpAsState( }
targetValue = when { }
state.dismissDirection == DismissDirection.EndToStart && val startCorners by animateDpAsState(
animateCorners -> 8.dp targetValue = when {
state.dismissDirection == DismissDirection.StartToEnd &&
animateCorners -> 8.dp
else -> 0.dp else -> 0.dp
}, label = "endCorners" }, label = "startCorners"
) )
val endCorners by animateDpAsState(
targetValue = when {
state.dismissDirection == DismissDirection.EndToStart &&
animateCorners -> 8.dp
Box( else -> 0.dp
modifier = Modifier.height(IntrinsicSize.Min) }, label = "endCorners"
) {
Surface(
modifier = Modifier
.fillMaxSize()
.padding(
vertical = min(
size / 4f,
4.dp
)
)
.clip(RoundedCornerShape(size)),
color = MaterialTheme.colorScheme.surface,
shape = RoundedCornerShape(
topStart = startCorners,
bottomStart = startCorners,
topEnd = endCorners,
bottomEnd = endCorners,
),
) {
// nothing
}
Box(
modifier = Modifier.padding(vertical = 4.dp)
) {
HistoryItem(
coil = coil,
history = item.historyItemModel!!,
onClick = { navigateToDetails(item.historyItemModel!!.manga.id) },
) )
Box(
modifier = Modifier.height(IntrinsicSize.Min)
) {
Surface(
modifier = Modifier
.fillMaxSize()
.padding(
vertical = min(
size / 4f,
4.dp
)
)
.clip(RoundedCornerShape(size)),
color = MaterialTheme.colorScheme.surface,
shape = RoundedCornerShape(
topStart = startCorners,
bottomStart = startCorners,
topEnd = endCorners,
bottomEnd = endCorners,
),
) {
// nothing
}
Box(
modifier = Modifier.padding(vertical = 4.dp)
) {
HistoryItem(
coil = coil,
history = item.historyItemModel!!,
onClick = { navigateToDetails(item.historyItemModel!!.manga.id) },
)
}
}
} }
} }
} }
@ -231,12 +245,5 @@ fun HistoryView(
} }
} }
} }
if (history.isEmpty()) {
EmptyScreen(
icon = Icons.Outlined.History,
title = R.string.empty_history_title,
description = R.string.empty_history_description
)
}
} }
} }

@ -108,7 +108,7 @@ fun BackupRestoreView(
return@rememberLauncherForActivityResult return@rememberLauncherForActivityResult
} }
navigateToRestoreScreen(uri.toString()) restoreViewModel.restore(uri)
} }
val showDirectoryAlert = val showDirectoryAlert =

@ -9,10 +9,12 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AccessTime import androidx.compose.material.icons.outlined.AccessTime
import androidx.compose.material.icons.outlined.Restore import androidx.compose.material.icons.outlined.Restore
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -28,7 +30,7 @@ import org.xtimms.tokusho.core.components.ScaffoldWithTopAppBar
import org.xtimms.tokusho.sections.settings.about.ProgressIndicatorButton import org.xtimms.tokusho.sections.settings.about.ProgressIndicatorButton
import org.xtimms.tokusho.utils.DeviceUtil import org.xtimms.tokusho.utils.DeviceUtil
const val RESTORE_ARGUMENT = "{source}" const val RESTORE_ARGUMENT = "{file}"
const val RESTORE_DESTINATION = "restore/?file=${RESTORE_ARGUMENT}" const val RESTORE_DESTINATION = "restore/?file=${RESTORE_ARGUMENT}"
@Composable @Composable
@ -38,7 +40,8 @@ fun RestoreItemsView(
navigateBack: () -> Unit, navigateBack: () -> Unit,
) { ) {
val items = restoreViewModel.availableEntries.collectAsStateWithLifecycle() val items by restoreViewModel.availableEntries.collectAsStateWithLifecycle(emptyList())
val backupDate by restoreViewModel.backupDate.collectAsStateWithLifecycle(null)
ScaffoldWithTopAppBar( ScaffoldWithTopAppBar(
title = stringResource(R.string.restore_from_backup), title = stringResource(R.string.restore_from_backup),
@ -61,16 +64,16 @@ fun RestoreItemsView(
item { item {
PreferencesHintCard( PreferencesHintCard(
title = stringResource(id = R.string.backup_creation_date), title = stringResource(id = R.string.backup_creation_date),
description = restoreViewModel.backupDate.value.toString(), description = backupDate.toString(),
icon = Icons.Outlined.AccessTime icon = Icons.Outlined.AccessTime
) )
} }
items( for (item in items) {
count = 5 item {
) { BackupItem(
BackupItem( title = item.name.name
title = it.toString() )
) }
} }
item { item {
var isLoading by remember { mutableStateOf(false) } var isLoading by remember { mutableStateOf(false) }
@ -89,7 +92,7 @@ fun RestoreItemsView(
icon = Icons.Outlined.Restore, icon = Icons.Outlined.Restore,
isLoading = isLoading isLoading = isLoading
) { ) {
restoreViewModel.restore() // restoreViewModel.restore()
} }
} }
} }

@ -1,6 +1,7 @@
package org.xtimms.tokusho.sections.settings.backup package org.xtimms.tokusho.sections.settings.backup
import android.content.Context import android.content.Context
import android.net.Uri
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
@ -25,13 +26,14 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class RestoreViewModel @Inject constructor( class RestoreViewModel @Inject constructor(
savedStateHandle: SavedStateHandle, private val savedStateHandle: SavedStateHandle,
private val repository: BackupRepository, private val repository: BackupRepository,
@ApplicationContext context: Context, @ApplicationContext val context: Context,
) : KotatsuBaseViewModel() { ) : KotatsuBaseViewModel() {
private val backupInput = SuspendLazy { private val backupInput = SuspendLazy {
val uri = savedStateHandle.get<String>(RESTORE_ARGUMENT)?.toUriOrNull() ?: throw FileNotFoundException() val uri = savedStateHandle.get<String>(RESTORE_ARGUMENT)?.toUriOrNull()
?: throw FileNotFoundException()
val contentResolver = context.contentResolver val contentResolver = context.contentResolver
runInterruptible(Dispatchers.IO) { runInterruptible(Dispatchers.IO) {
val tempFile = File.createTempFile("backup_", ".tmp") val tempFile = File.createTempFile("backup_", ".tmp")
@ -74,57 +76,68 @@ class RestoreViewModel @Inject constructor(
} }
fun onItemClick(item: BackupEntryModel) { fun onItemClick(item: BackupEntryModel) {
val map = availableEntries.value.associateByTo(EnumMap(BackupEntry.Name::class.java)) { it.name } val map =
availableEntries.value.associateByTo(EnumMap(BackupEntry.Name::class.java)) { it.name }
map[item.name] = item.copy(isChecked = !item.isChecked) map[item.name] = item.copy(isChecked = !item.isChecked)
map.validate() map.validate()
availableEntries.value = map.values.sortedBy { it.name.ordinal } availableEntries.value = map.values.sortedBy { it.name.ordinal }
} }
fun restore() { fun restore(uri: Uri) {
launchLoadingJob { launchLoadingJob {
val backup = backupInput.get() val contentResolver = context.contentResolver
val checkedItems = availableEntries.value.mapNotNullTo(EnumSet.noneOf(BackupEntry.Name::class.java)) { val tempFile = File.createTempFile("backup_", ".tmp")
if (it.isChecked) it.name else null (contentResolver.openInputStream(uri) ?: throw FileNotFoundException()).use { input ->
} tempFile.outputStream().use { output ->
input.copyTo(output)
}
}
val backupInput = BackupZipInput(tempFile)
val backup: BackupZipInput = backupInput
val checkedItems =
availableEntries.value.mapNotNullTo(EnumSet.noneOf(BackupEntry.Name::class.java)) {
if (it.isChecked) it.name else null
}
val result = CompositeResult() val result = CompositeResult()
val step = 1f / 5f val step = 1f / 5f
progress.value = 0f progress.value = 0f
if (BackupEntry.Name.HISTORY in checkedItems) { //if (BackupEntry.Name.HISTORY in checkedItems) {
backup.getEntry(BackupEntry.Name.HISTORY)?.let { backup.getEntry(BackupEntry.Name.HISTORY)?.let {
result += repository.restoreHistory(it) result += repository.restoreHistory(it)
}
} }
//}
progress.value += step progress.value += step
if (BackupEntry.Name.CATEGORIES in checkedItems) { //if (BackupEntry.Name.CATEGORIES in checkedItems) {
backup.getEntry(BackupEntry.Name.CATEGORIES)?.let { backup.getEntry(BackupEntry.Name.CATEGORIES)?.let {
result += repository.restoreCategories(it) result += repository.restoreCategories(it)
}
} }
//}
progress.value += step progress.value += step
if (BackupEntry.Name.FAVOURITES in checkedItems) { //if (BackupEntry.Name.FAVOURITES in checkedItems) {
backup.getEntry(BackupEntry.Name.FAVOURITES)?.let { backup.getEntry(BackupEntry.Name.FAVOURITES)?.let {
result += repository.restoreFavourites(it) result += repository.restoreFavourites(it)
}
} }
//}
progress.value += step progress.value += step
if (BackupEntry.Name.BOOKMARKS in checkedItems) { //if (BackupEntry.Name.BOOKMARKS in checkedItems) {
backup.getEntry(BackupEntry.Name.BOOKMARKS)?.let { backup.getEntry(BackupEntry.Name.BOOKMARKS)?.let {
result += repository.restoreBookmarks(it) result += repository.restoreBookmarks(it)
}
} }
//}
progress.value += step progress.value += step
if (BackupEntry.Name.SOURCES in checkedItems) { //if (BackupEntry.Name.SOURCES in checkedItems) {
backup.getEntry(BackupEntry.Name.SOURCES)?.let { backup.getEntry(BackupEntry.Name.SOURCES)?.let {
result += repository.restoreSources(it) result += repository.restoreSources(it)
}
} }
//}
progress.value = 1f progress.value = 1f
backup.cleanupAsync()
onRestoreDone.call(result) onRestoreDone.call(result)
} }
} }
@ -142,7 +155,8 @@ class RestoreViewModel @Inject constructor(
} }
} else { } else {
if (favorites.isEnabled) { if (favorites.isEnabled) {
this[BackupEntry.Name.FAVOURITES] = favorites.copy(isEnabled = false, isChecked = false) this[BackupEntry.Name.FAVOURITES] =
favorites.copy(isEnabled = false, isChecked = false)
} }
} }
} }

@ -25,7 +25,6 @@ fun ShelfPager(
coil: ImageLoader, coil: ImageLoader,
state: PagerState, state: PagerState,
contentPadding: PaddingValues, contentPadding: PaddingValues,
searchQuery: String?,
getShelfForPage: (Int) -> List<ShelfManga>, getShelfForPage: (Int) -> List<ShelfManga>,
navigateToDetails: (ShelfManga) -> Unit, navigateToDetails: (ShelfManga) -> Unit,
) { ) {
@ -41,7 +40,6 @@ fun ShelfPager(
val library = getShelfForPage(page) val library = getShelfForPage(page)
if (library.isEmpty()) { if (library.isEmpty()) {
ShelfPagerEmptyScreen( ShelfPagerEmptyScreen(
searchQuery = searchQuery,
contentPadding = contentPadding, contentPadding = contentPadding,
) )
return@HorizontalPager return@HorizontalPager
@ -61,14 +59,8 @@ fun ShelfPager(
@Composable @Composable
private fun ShelfPagerEmptyScreen( private fun ShelfPagerEmptyScreen(
searchQuery: String?,
contentPadding: PaddingValues, contentPadding: PaddingValues,
) { ) {
val msg = when {
!searchQuery.isNullOrEmpty() -> R.string.no_results_found
else -> R.string.information_no_manga_category
}
Column( Column(
modifier = Modifier modifier = Modifier
.padding(contentPadding + PaddingValues(8.dp)) .padding(contentPadding + PaddingValues(8.dp))
@ -78,7 +70,7 @@ private fun ShelfPagerEmptyScreen(
EmptyScreen( EmptyScreen(
icon = Icons.Outlined.Close, icon = Icons.Outlined.Close,
title = R.string.empty_here, title = R.string.empty_here,
description = msg, description = R.string.information_no_manga_category,
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
) )
} }

@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -110,7 +109,6 @@ fun ShelfViewContent(
coil = coil, coil = coil,
state = pagerState, state = pagerState,
contentPadding = PaddingValues(bottom = padding.calculateBottomPadding()), contentPadding = PaddingValues(bottom = padding.calculateBottomPadding()),
searchQuery = "",
getShelfForPage = { mangas }, getShelfForPage = { mangas },
navigateToDetails = onClickManga navigateToDetails = onClickManga
) )

@ -3,18 +3,12 @@ package org.xtimms.tokusho.sections.shelf
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus import kotlinx.coroutines.plus
import org.xtimms.tokusho.core.base.viewmodel.BaseViewModel
import org.xtimms.tokusho.core.base.viewmodel.KotatsuBaseViewModel import org.xtimms.tokusho.core.base.viewmodel.KotatsuBaseViewModel
import org.xtimms.tokusho.data.repository.FavouritesRepository import org.xtimms.tokusho.data.repository.FavouritesRepository
import org.xtimms.tokusho.utils.lang.mapItems import org.xtimms.tokusho.utils.lang.mapItems
@ -28,11 +22,10 @@ class ShelfViewModel @Inject constructor(
private val mangasStateFlow = favouritesRepository.observeAll(1) private val mangasStateFlow = favouritesRepository.observeAll(1)
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, null) .stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, null)
private val categoriesStateFlow = favouritesRepository.observeCategoriesForLibrary() private val categoriesStateFlow = favouritesRepository.observeCategoriesForLibrary()
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, null) .stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, null)
val mangaCount = favouritesRepository.observeMangaCount() val mangaCount = favouritesRepository.observeMangaCountInCategory(1)
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, null) .stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, null)
val categories = categoriesStateFlow val categories = categoriesStateFlow

Loading…
Cancel
Save