[Manatoki] Implemented next episode button in reader
[Hitomi] Adjusted Search result padding
This commit is contained in:
@@ -520,9 +520,16 @@ fun ReaderItem(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val modifier = when {
|
val modifier = when {
|
||||||
imageSize == null -> Modifier.weight(1f).height(heightDp)
|
imageSize == null -> Modifier
|
||||||
readerOptions.padding -> Modifier.fillMaxHeight().widthIn(0.dp, widthDp/images.size).aspectRatio(imageSize.width/imageSize.height)
|
.weight(1f)
|
||||||
readerOptions.orientation.isVertical -> Modifier.weight(1f).aspectRatio(imageSize.width/imageSize.height)
|
.height(heightDp)
|
||||||
|
readerOptions.padding -> Modifier
|
||||||
|
.fillMaxHeight()
|
||||||
|
.widthIn(0.dp, widthDp / images.size)
|
||||||
|
.aspectRatio(imageSize.width / imageSize.height)
|
||||||
|
readerOptions.orientation.isVertical -> Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(imageSize.width / imageSize.height)
|
||||||
else -> Modifier.aspectRatio(imageSize.width/imageSize.height)
|
else -> Modifier.aspectRatio(imageSize.width/imageSize.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,6 +634,7 @@ fun LazyListScope.ReaderLazyListContent(
|
|||||||
fun ReaderBase(
|
fun ReaderBase(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
model: ReaderBaseViewModel,
|
model: ReaderBaseViewModel,
|
||||||
|
listState: LazyListState = rememberLazyListState(),
|
||||||
onScroll: (direction: Float) -> Unit = { }
|
onScroll: (direction: Float) -> Unit = { }
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -687,7 +695,6 @@ fun ReaderBase(
|
|||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
var listSize: Size? by remember { mutableStateOf(null) }
|
var listSize: Size? by remember { mutableStateOf(null) }
|
||||||
val listState = rememberLazyListState()
|
|
||||||
|
|
||||||
val nestedScrollConnection = remember { object: NestedScrollConnection {
|
val nestedScrollConnection = remember { object: NestedScrollConnection {
|
||||||
override suspend fun onPreFling(available: Velocity): Velocity {
|
override suspend fun onPreFling(available: Velocity): Velocity {
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ fun DetailedSearchResult(
|
|||||||
|
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(8.dp)
|
.padding(8.dp, 4.dp)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable { onClick(result) },
|
.clickable { onClick(result) },
|
||||||
elevation = 4.dp
|
elevation = 4.dp
|
||||||
|
|||||||
@@ -18,9 +18,7 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil.sources.manatoki.composable
|
package xyz.quaver.pupil.sources.manatoki.composable
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
@@ -30,15 +28,11 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
|
|||||||
import androidx.compose.foundation.interaction.PressInteraction
|
import androidx.compose.foundation.interaction.PressInteraction
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.lazy.LazyListItemInfo
|
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.List
|
import androidx.compose.material.icons.filled.*
|
||||||
import androidx.compose.material.icons.filled.NavigateBefore
|
|
||||||
import androidx.compose.material.icons.filled.Star
|
|
||||||
import androidx.compose.material.icons.filled.StarOutline
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
@@ -115,7 +109,8 @@ fun Reader(navController: NavController) {
|
|||||||
).calculateBottomPadding().toPx()
|
).calculateBottomPadding().toPx()
|
||||||
}
|
}
|
||||||
|
|
||||||
val listState = rememberLazyListState()
|
val bottomSheetListState = rememberLazyListState()
|
||||||
|
val readerListState = rememberLazyListState()
|
||||||
|
|
||||||
var scrollDirection by remember { mutableStateOf(0f) }
|
var scrollDirection by remember { mutableStateOf(0f) }
|
||||||
|
|
||||||
@@ -140,17 +135,10 @@ fun Reader(navController: NavController) {
|
|||||||
mangaListingListSize = it
|
mangaListingListSize = it
|
||||||
},
|
},
|
||||||
rippleInteractionSource = mangaListingRippleInteractionSource,
|
rippleInteractionSource = mangaListingRippleInteractionSource,
|
||||||
listState = listState
|
listState = bottomSheetListState
|
||||||
) {
|
) {
|
||||||
coroutineScope.launch {
|
navController.navigate("manatoki.net/reader/$it") {
|
||||||
client.getItem(
|
popUpTo("manatoki.net/")
|
||||||
it,
|
|
||||||
onReader = {
|
|
||||||
navController.navigate("manatoki.net/reader/${it.itemID}") {
|
|
||||||
popUpTo("manatoki.net/")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,70 +193,96 @@ fun Reader(navController: NavController) {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
val scale by animateFloatAsState(if (model.fullscreen || scrollDirection < 0f) 0f else 1f)
|
val showNextButton by derivedStateOf {
|
||||||
|
(readerInfo?.nextItemID?.isNotEmpty() == true) && with (readerListState.layoutInfo) {
|
||||||
|
visibleItemsInfo.lastOrNull()?.index == totalItemsCount-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val scale by animateFloatAsState(if (!showNextButton && (model.fullscreen || scrollDirection < 0f)) 0f else 1f)
|
||||||
|
|
||||||
if (scale > 0f)
|
if (scale > 0f)
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
modifier = Modifier.navigationBarsPadding().scale(scale),
|
modifier = Modifier
|
||||||
|
.navigationBarsPadding()
|
||||||
|
.scale(scale),
|
||||||
onClick = {
|
onClick = {
|
||||||
readerInfo?.let {
|
readerInfo?.let {
|
||||||
coroutineScope.launch {
|
if (showNextButton) {
|
||||||
sheetState.show()
|
navController.navigate("manatoki.net/reader/${it.nextItemID}") {
|
||||||
}
|
popUpTo("manatoki.net/")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
coroutineScope.launch {
|
||||||
|
sheetState.show()
|
||||||
|
}
|
||||||
|
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
if (mangaListing?.itemID != it.listingItemID)
|
if (mangaListing?.itemID != it.listingItemID)
|
||||||
client.getItem(it.listingItemID, onListing = {
|
client.getItem(it.listingItemID, onListing = {
|
||||||
mangaListing = it
|
mangaListing = it
|
||||||
|
|
||||||
mangaListingRippleInteractionSource.addAll(
|
mangaListingRippleInteractionSource.addAll(
|
||||||
List(max(it.entries.size - mangaListingRippleInteractionSource.size, 0)) {
|
List(
|
||||||
MutableInteractionSource()
|
max(
|
||||||
}
|
it.entries.size - mangaListingRippleInteractionSource.size,
|
||||||
)
|
0
|
||||||
|
|
||||||
coroutineScope.launch {
|
|
||||||
while (listState.layoutInfo.totalItemsCount != it.entries.size) {
|
|
||||||
delay(100)
|
|
||||||
}
|
|
||||||
|
|
||||||
val targetIndex = it.entries.indexOfFirst { it.itemID == itemID }
|
|
||||||
|
|
||||||
listState.scrollToItem(targetIndex)
|
|
||||||
|
|
||||||
mangaListingListSize?.let { sheetSize ->
|
|
||||||
val targetItem = listState.layoutInfo.visibleItemsInfo.first {
|
|
||||||
it.key == itemID
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetItem.offset == 0) {
|
|
||||||
listState.animateScrollBy(
|
|
||||||
-(sheetSize.height - navigationBarsPadding - targetItem.size)
|
|
||||||
)
|
)
|
||||||
|
) {
|
||||||
|
MutableInteractionSource()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
coroutineScope.launch {
|
||||||
|
while (bottomSheetListState.layoutInfo.totalItemsCount != it.entries.size) {
|
||||||
|
delay(100)
|
||||||
}
|
}
|
||||||
|
|
||||||
delay(200)
|
val targetIndex =
|
||||||
|
it.entries.indexOfFirst { it.itemID == itemID }
|
||||||
|
|
||||||
with (mangaListingRippleInteractionSource[targetIndex]) {
|
bottomSheetListState.scrollToItem(targetIndex)
|
||||||
val interaction = PressInteraction.Press(
|
|
||||||
Offset(sheetSize.width/2, targetItem.size/2f)
|
mangaListingListSize?.let { sheetSize ->
|
||||||
)
|
val targetItem =
|
||||||
|
bottomSheetListState.layoutInfo.visibleItemsInfo.first {
|
||||||
|
it.key == itemID
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetItem.offset == 0) {
|
||||||
|
bottomSheetListState.animateScrollBy(
|
||||||
|
-(sheetSize.height - navigationBarsPadding - targetItem.size)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(200)
|
||||||
|
|
||||||
|
with(mangaListingRippleInteractionSource[targetIndex]) {
|
||||||
|
val interaction =
|
||||||
|
PressInteraction.Press(
|
||||||
|
Offset(
|
||||||
|
sheetSize.width / 2,
|
||||||
|
targetItem.size / 2f
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
emit(interaction)
|
emit(interaction)
|
||||||
emit(PressInteraction.Release(interaction))
|
emit(
|
||||||
|
PressInteraction.Release(
|
||||||
|
interaction
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Default.List,
|
if (showNextButton) Icons.Default.NavigateNext else Icons.Default.List,
|
||||||
|
|
||||||
contentDescription = null
|
contentDescription = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -277,6 +291,7 @@ fun Reader(navController: NavController) {
|
|||||||
ReaderBase(
|
ReaderBase(
|
||||||
Modifier.padding(contentPadding),
|
Modifier.padding(contentPadding),
|
||||||
model = model,
|
model = model,
|
||||||
|
listState = readerListState,
|
||||||
onScroll = { scrollDirection = it }
|
onScroll = { scrollDirection = it }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,9 @@ data class ReaderInfo(
|
|||||||
val itemID: String,
|
val itemID: String,
|
||||||
val title: String,
|
val title: String,
|
||||||
val urls: List<String>,
|
val urls: List<String>,
|
||||||
val listingItemID: String
|
val listingItemID: String,
|
||||||
|
val prevItemID: String,
|
||||||
|
val nextItemID: String
|
||||||
): Parcelable
|
): Parcelable
|
||||||
|
|
||||||
@ExperimentalMaterialApi
|
@ExperimentalMaterialApi
|
||||||
@@ -142,7 +144,7 @@ suspend fun HttpClient.getItem(
|
|||||||
}.toString()
|
}.toString()
|
||||||
|
|
||||||
val urls = Jsoup.parse(htmlData)
|
val urls = Jsoup.parse(htmlData)
|
||||||
.select("img[^data-]:not([style])")
|
.select("img[^data-]:not([style]):not([src*=loading])").also { Log.d("PUPILD", it.size.toString()) }
|
||||||
.map {
|
.map {
|
||||||
it.attributes()
|
it.attributes()
|
||||||
.first { it.key.startsWith("data-") }
|
.first { it.key.startsWith("data-") }
|
||||||
@@ -154,11 +156,29 @@ suspend fun HttpClient.getItem(
|
|||||||
val listingItemID = doc.select("a:contains(전체목록)").first()!!.attr("href")
|
val listingItemID = doc.select("a:contains(전체목록)").first()!!.attr("href")
|
||||||
.takeLastWhile { it != '/' }
|
.takeLastWhile { it != '/' }
|
||||||
|
|
||||||
|
val prevItemID = doc.getElementById("goPrevBtn")!!.attr("href")
|
||||||
|
.let {
|
||||||
|
if (it.contains('?'))
|
||||||
|
it.dropLastWhile { it != '?' }.drop(1)
|
||||||
|
else it
|
||||||
|
}
|
||||||
|
.takeLastWhile { it != '/' }
|
||||||
|
|
||||||
|
val nextItemID = doc.getElementById("goNextBtn")!!.attr("href")
|
||||||
|
.let {
|
||||||
|
if (it.contains('?'))
|
||||||
|
it.dropLastWhile { it != '?' }.drop(1)
|
||||||
|
else it
|
||||||
|
}
|
||||||
|
.takeLastWhile { it != '/' }
|
||||||
|
|
||||||
val readerInfo = ReaderInfo(
|
val readerInfo = ReaderInfo(
|
||||||
itemID,
|
itemID,
|
||||||
title,
|
title,
|
||||||
urls,
|
urls,
|
||||||
listingItemID
|
listingItemID,
|
||||||
|
prevItemID,
|
||||||
|
nextItemID
|
||||||
)
|
)
|
||||||
|
|
||||||
synchronized(cache) {
|
synchronized(cache) {
|
||||||
|
|||||||
Reference in New Issue
Block a user