This commit is contained in:
tom5079
2021-12-17 22:34:46 +09:00
parent e7debfec46
commit 98fda1a53f
7 changed files with 155 additions and 89 deletions

View File

@@ -2,6 +2,7 @@ package xyz.quaver.pupil.db
import androidx.lifecycle.LiveData
import androidx.room.*
import xyz.quaver.pupil.sources.ItemInfo
@Entity(primaryKeys = ["source", "itemID"])
data class Bookmark(
@@ -22,10 +23,17 @@ interface BookmarkDao {
fun contains(source: String, itemID: String): LiveData<Boolean>
fun contains(bookmark: Bookmark) = contains(bookmark.source, bookmark.itemID)
fun contains(itemInfo: ItemInfo) = contains(itemInfo.source, itemInfo.itemID)
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(bookmark: Bookmark)
suspend fun insert(source: String, itemID: String) = insert(Bookmark(source, itemID))
suspend fun insert(itemInfo: ItemInfo) = insert(Bookmark(itemInfo.source, itemInfo.itemID))
@Delete
suspend fun delete(bookmark: Bookmark)
suspend fun delete(source: String, itemID: String) = delete(Bookmark(source, itemID))
suspend fun delete(itemInfo: ItemInfo) = delete(Bookmark(itemInfo.source, itemInfo.itemID))
}

View File

@@ -35,8 +35,7 @@ data class SearchResultEvent(val type: Type, val itemID: String, val payload: Pa
enum class Type {
OPEN_READER,
OPEN_DETAILS,
NEW_QUERY,
TOGGLE_FAVORITES
NEW_QUERY
}
}

View File

@@ -29,6 +29,8 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Female
import androidx.compose.material.icons.filled.Male
import androidx.compose.material.icons.filled.Star
import androidx.compose.material.icons.filled.StarOutline
import androidx.compose.material.icons.outlined.Star
import androidx.compose.material.icons.outlined.StarOutline
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
@@ -374,10 +376,12 @@ class Hitomi(app: Application) : Source(), DIAware {
var group by remember { mutableStateOf(emptyList<String>()) }
var pageCount by remember { mutableStateOf("-") }
val bookmark by bookmarkDao.contains(itemInfo).observeAsState(false)
LaunchedEffect(itemInfo) {
launch(Dispatchers.Default) {
itemInfo.getPageCount()?.run {
pageCount = "${this}P"
itemInfo.getPageCount()?.let {
pageCount = "${it}P"
}
}
@@ -468,35 +472,32 @@ class Hitomi(app: Application) : Source(), DIAware {
Divider(
thickness = 1.dp,
modifier = Modifier.padding(0.dp, 8.dp)
modifier = Modifier.padding(0.dp, 8.dp, 0.dp, 0.dp)
)
Box(
Modifier
.fillMaxWidth()
.wrapContentHeight()
Row(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
itemInfo.itemID,
color = MaterialTheme.colors.onSurface,
modifier = Modifier
.padding(8.dp)
.align(Alignment.CenterStart)
)
Text(itemInfo.itemID)
Text(
pageCount,
color = MaterialTheme.colors.onSurface,
modifier = Modifier.align(Alignment.Center)
)
Text(pageCount)
Image(
painterResource(id = R.drawable.ic_star_empty),
contentDescription = "Favorite",
Icon(
if (bookmark) Icons.Default.Star else Icons.Default.StarOutline,
contentDescription = null,
tint = Orange500,
modifier = Modifier
.size(32.dp)
.padding(4.dp)
.align(Alignment.CenterEnd)
.clickable {
CoroutineScope(Dispatchers.IO).launch {
if (bookmark) bookmarkDao.delete(itemInfo)
else bookmarkDao.insert(itemInfo)
}
}
)
}
}

View File

@@ -30,6 +30,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Female
import androidx.compose.material.icons.filled.Male
import androidx.compose.material.icons.filled.Star
import androidx.compose.material.icons.filled.StarOutline
import androidx.compose.material.icons.outlined.StarOutline
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
@@ -329,68 +330,101 @@ class Hiyobi_io(app: Application): Source(), DIAware {
override fun SearchResult(itemInfo: ItemInfo, onEvent: (SearchResultEvent) -> Unit) {
itemInfo as HiyobiItemInfo
val bookmark by bookmarkDao.contains(itemInfo).observeAsState(false)
val painter = rememberImagePainter(itemInfo.thumbnail)
Row(
Column(
modifier = Modifier.clickable {
onEvent(SearchResultEvent(SearchResultEvent.Type.OPEN_READER, itemInfo.itemID, itemInfo))
}
) {
Image(
painter = painter,
contentDescription = null,
modifier = Modifier
.requiredWidth(150.dp)
.aspectRatio(
with(painter.intrinsicSize) { if (this == Size.Unspecified) 1f else width / height },
true
)
.padding(0.dp, 0.dp, 8.dp, 0.dp)
.align(Alignment.CenterVertically),
contentScale = ContentScale.FillWidth
)
Column {
Text(
itemInfo.title,
style = MaterialTheme.typography.h6,
color = MaterialTheme.colors.onSurface
Row {
Image(
painter = painter,
contentDescription = null,
modifier = Modifier
.requiredWidth(150.dp)
.aspectRatio(
with(painter.intrinsicSize) { if (this == Size.Unspecified) 1f else width / height },
true
)
.padding(0.dp, 0.dp, 8.dp, 0.dp)
.align(Alignment.CenterVertically),
contentScale = ContentScale.FillWidth
)
val artistStringBuilder = StringBuilder()
with (itemInfo.artists) {
if (this.isNotEmpty())
artistStringBuilder.append(this.joinToString(", ") { it.wordCapitalize() })
}
if (artistStringBuilder.isNotEmpty())
Column {
Text(
artistStringBuilder.toString(),
style = MaterialTheme.typography.subtitle1,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6F)
itemInfo.title,
style = MaterialTheme.typography.h6,
color = MaterialTheme.colors.onSurface
)
if (itemInfo.series.isNotEmpty())
val artistStringBuilder = StringBuilder()
with(itemInfo.artists) {
if (this.isNotEmpty())
artistStringBuilder.append(this.joinToString(", ") { it.wordCapitalize() })
}
if (artistStringBuilder.isNotEmpty())
Text(
artistStringBuilder.toString(),
style = MaterialTheme.typography.subtitle1,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6F)
)
if (itemInfo.series.isNotEmpty())
Text(
stringResource(
id = R.string.galleryblock_series,
itemInfo.series.joinToString { it.wordCapitalize() }
),
style = MaterialTheme.typography.body2,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6F)
)
Text(
stringResource(
id = R.string.galleryblock_series,
itemInfo.series.joinToString { it.wordCapitalize() }
),
stringResource(id = R.string.galleryblock_type, itemInfo.type),
style = MaterialTheme.typography.body2,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6F)
)
Text(
stringResource(id = R.string.galleryblock_type, itemInfo.type),
style = MaterialTheme.typography.body2,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6F)
)
key(itemInfo.tags) {
TagGroup(tags = itemInfo.tags)
key(itemInfo.tags) {
TagGroup(tags = itemInfo.tags)
}
}
}
Divider(
thickness = 1.dp,
modifier = Modifier.padding(0.dp, 8.dp, 0.dp, 0.dp)
)
Row(
modifier = Modifier.padding(8.dp).fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(itemInfo.itemID)
Text("${itemInfo.pageCount}P")
Icon(
if (bookmark) Icons.Default.Star else Icons.Default.StarOutline,
contentDescription = null,
tint = Orange500,
modifier = Modifier
.size(32.dp)
.clickable {
CoroutineScope(Dispatchers.IO).launch {
if (bookmark) bookmarkDao.delete(itemInfo)
else bookmarkDao.insert(itemInfo)
}
}
)
}
}
}

View File

@@ -24,10 +24,7 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.*
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
@@ -36,7 +33,10 @@ import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.BrokenImage
import androidx.compose.material.icons.filled.Fullscreen
import androidx.compose.material.icons.filled.Star
import androidx.compose.material.icons.filled.StarOutline
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -45,6 +45,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
@@ -60,6 +61,7 @@ import xyz.quaver.pupil.R
import xyz.quaver.pupil.ui.composable.FloatingActionButtonState
import xyz.quaver.pupil.ui.composable.MultipleFloatingActionButton
import xyz.quaver.pupil.ui.composable.SubFabItem
import xyz.quaver.pupil.ui.theme.Orange500
import xyz.quaver.pupil.ui.theme.PupilTheme
import xyz.quaver.pupil.ui.viewmodel.ReaderViewModel
import xyz.quaver.pupil.util.FileXImageSource
@@ -83,6 +85,7 @@ class ReaderActivity : ComponentActivity(), DIAware {
var isFABExpanded by remember { mutableStateOf(FloatingActionButtonState.COLLAPSED) }
val imageSources = remember { mutableStateListOf<ImageSource?>() }
val states = remember { mutableStateListOf<SubSampledImageState>() }
val bookmark by model.bookmark.observeAsState(false)
val scaffoldState = rememberScaffoldState()
val snackbarCoroutineScope = rememberCoroutineScope()
@@ -130,16 +133,32 @@ class ReaderActivity : ComponentActivity(), DIAware {
title = {
Text(
model.title ?: stringResource(R.string.reader_loading),
color = MaterialTheme.colors.onSecondary
color = MaterialTheme.colors.onSecondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
actions = {
model.sourceIcon?.let { sourceIcon ->
Image(
modifier = Modifier.size(36.dp),
painter = painterResource(id = sourceIcon),
contentDescription = null
Row(
modifier = Modifier.padding(16.dp, 0.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(24.dp)
) {
Icon(
if (bookmark) Icons.Default.Star else Icons.Default.StarOutline,
contentDescription = null,
tint = Orange500,
modifier = Modifier.size(24.dp).clickable {
model.toggleBookmark()
}
)
model.sourceIcon?.let { sourceIcon ->
Image(
modifier = Modifier.size(24.dp),
painter = painterResource(id = sourceIcon),
contentDescription = null
)
}
}
}
)

View File

@@ -132,7 +132,6 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
for (result in channel) {
yield()
searchResults.add(result)
logger.info { result.toString() }
}
loading = false

View File

@@ -43,6 +43,7 @@ import xyz.quaver.pupil.sources.ItemInfo
import xyz.quaver.pupil.sources.Source
import xyz.quaver.pupil.util.NetworkCache
import xyz.quaver.pupil.util.source
import java.nio.file.Files.delete
@Suppress("UNCHECKED_CAST")
class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
@@ -60,6 +61,9 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
private val historyDao = database.historyDao()
private val bookmarkDao = database.bookmarkDao()
lateinit var bookmark: LiveData<Boolean>
private set
var error by mutableStateOf(false)
private set
@@ -108,6 +112,8 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
itemID = intent.getStringExtra("id") ?: error("Invalid itemID")
title = intent.getParcelableExtra<ItemInfo>("payload")?.title
}
bookmark = bookmarkDao.contains(source!!.name, itemID!!)
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -203,14 +209,14 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
}
fun toggleBookmark() {
val bookmark = source?.let { source -> itemID?.let { itemID -> Bookmark(source.name, itemID) } } ?: return
CoroutineScope(Dispatchers.IO).launch {
if (bookmarkDao.contains(bookmark).value ?: return@launch)
bookmarkDao.delete(bookmark)
else
bookmarkDao.insert(bookmark)
}
source?.name?.let { source ->
itemID?.let { itemID ->
bookmark.value?.let { bookmark ->
CoroutineScope(Dispatchers.IO).launch {
if (bookmark) bookmarkDao.delete(source, itemID)
else bookmarkDao.insert(source, itemID)
}
} } }
}
override fun onCleared() {