Prepare to export sources
This commit is contained in:
@@ -20,7 +20,7 @@ android {
|
|||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 31
|
targetSdk = 31
|
||||||
versionCode = 600
|
versionCode = 600
|
||||||
versionName = "6.0.0-alpha1"
|
versionName = VERSION
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"filters": [],
|
"filters": [],
|
||||||
"attributes": [],
|
"attributes": [],
|
||||||
"versionCode": 600,
|
"versionCode": 600,
|
||||||
"versionName": "6.0.0-alpha1",
|
"versionName": "6.0.0-alpha02",
|
||||||
"outputFile": "app-release.apk"
|
"outputFile": "app-release.apk"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -243,7 +243,10 @@ open class ReaderBaseViewModel(app: Application) : AndroidViewModel(app), DIAwar
|
|||||||
totalProgress++
|
totalProgress++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw IllegalArgumentException("Expected URL scheme 'http(s)' or 'content' but was '$scheme'")
|
else -> {
|
||||||
|
logger.warning(IllegalArgumentException("Expected URL scheme 'http(s)' or 'content' but was '$scheme'"))
|
||||||
|
progressList[index] = Float.NEGATIVE_INFINITY
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ interface FavoritesDao {
|
|||||||
suspend fun delete(item: String) = delete(Favorite(item))
|
suspend fun delete(item: String) = delete(Favorite(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Database(entities = [Favorite::class], version = 1)
|
@Database(entities = [Favorite::class], version = 1, exportSchema = false)
|
||||||
abstract class HitomiDatabase : RoomDatabase() {
|
abstract class HitomiDatabase : RoomDatabase() {
|
||||||
abstract fun favoritesDao(): FavoritesDao
|
abstract fun favoritesDao(): FavoritesDao
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,7 @@ package xyz.quaver.pupil.sources.manatoki
|
|||||||
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import java.sql.Timestamp
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
data class Favorite(
|
data class Favorite(
|
||||||
@@ -35,7 +36,9 @@ data class Bookmark(
|
|||||||
@Entity
|
@Entity
|
||||||
data class History(
|
data class History(
|
||||||
@PrimaryKey val itemID: String,
|
@PrimaryKey val itemID: String,
|
||||||
val page: Int
|
val parent: String,
|
||||||
|
val page: Int,
|
||||||
|
val timestamp: Long = System.currentTimeMillis()
|
||||||
)
|
)
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
@@ -59,10 +62,21 @@ interface BookmarkDao {
|
|||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface HistoryDao {
|
interface HistoryDao {
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
suspend fun insert(history: History)
|
||||||
|
suspend fun insert(itemID: String, parent: String, page: Int) = insert(History(itemID, parent, page))
|
||||||
|
|
||||||
|
@Query("DELETE FROM history WHERE itemID = :itemID")
|
||||||
|
suspend fun delete(itemID: String)
|
||||||
|
|
||||||
|
@Query("SELECT parent FROM (SELECT parent, max(timestamp) as t FROM history GROUP BY parent) ORDER BY t DESC")
|
||||||
|
fun getRecentManga(): Flow<List<String>>
|
||||||
|
|
||||||
|
@Query("SELECT itemID FROM history WHERE parent = :parent ORDER BY timestamp DESC")
|
||||||
|
suspend fun getAll(parent: String): List<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Database(entities = [Favorite::class, Bookmark::class, History::class], version = 1)
|
@Database(entities = [Favorite::class, Bookmark::class, History::class], version = 1, exportSchema = false)
|
||||||
abstract class ManatokiDatabase: RoomDatabase() {
|
abstract class ManatokiDatabase: RoomDatabase() {
|
||||||
abstract fun favoriteDao(): FavoriteDao
|
abstract fun favoriteDao(): FavoriteDao
|
||||||
abstract fun bookmarkDao(): BookmarkDao
|
abstract fun bookmarkDao(): BookmarkDao
|
||||||
|
|||||||
@@ -20,9 +20,13 @@ package xyz.quaver.pupil.sources.manatoki.composable
|
|||||||
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.*
|
||||||
|
import androidx.compose.foundation.gestures.animateScrollBy
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.interaction.PressInteraction
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyRow
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
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
|
||||||
@@ -33,8 +37,11 @@ import androidx.compose.runtime.*
|
|||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.geometry.Size
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
@@ -46,12 +53,15 @@ import com.google.accompanist.insets.rememberInsetsPaddingValues
|
|||||||
import com.google.accompanist.insets.ui.Scaffold
|
import com.google.accompanist.insets.ui.Scaffold
|
||||||
import com.google.accompanist.insets.ui.TopAppBar
|
import com.google.accompanist.insets.ui.TopAppBar
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.kodein.di.compose.rememberInstance
|
import org.kodein.di.compose.rememberInstance
|
||||||
import org.kodein.di.compose.rememberViewModel
|
import org.kodein.di.compose.rememberViewModel
|
||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
import xyz.quaver.pupil.proto.settingsDataStore
|
import xyz.quaver.pupil.proto.settingsDataStore
|
||||||
import xyz.quaver.pupil.sources.composable.SourceSelectDialog
|
import xyz.quaver.pupil.sources.composable.SourceSelectDialog
|
||||||
|
import xyz.quaver.pupil.sources.manatoki.ManatokiDatabase
|
||||||
import xyz.quaver.pupil.sources.manatoki.MangaListing
|
import xyz.quaver.pupil.sources.manatoki.MangaListing
|
||||||
import xyz.quaver.pupil.sources.manatoki.ReaderInfo
|
import xyz.quaver.pupil.sources.manatoki.ReaderInfo
|
||||||
import xyz.quaver.pupil.sources.manatoki.getItem
|
import xyz.quaver.pupil.sources.manatoki.getItem
|
||||||
@@ -64,15 +74,28 @@ fun Main(navController: NavController) {
|
|||||||
|
|
||||||
val client: HttpClient by rememberInstance()
|
val client: HttpClient by rememberInstance()
|
||||||
|
|
||||||
|
val database: ManatokiDatabase by rememberInstance()
|
||||||
|
val historyDao = remember { database.historyDao() }
|
||||||
|
val recent by remember { historyDao.getRecentManga() }.collectAsState(emptyList())
|
||||||
|
val recentManga = remember { mutableStateListOf<Thumbnail>() }
|
||||||
|
|
||||||
|
LaunchedEffect(recent) {
|
||||||
|
recentManga.clear()
|
||||||
|
|
||||||
|
recent.forEach {
|
||||||
|
if (isActive)
|
||||||
|
client.getItem(it, onListing = {
|
||||||
|
recentManga.add(
|
||||||
|
Thumbnail(it.itemID, it.title, it.thumbnail)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
|
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
|
||||||
var mangaListing: MangaListing? by rememberSaveable { mutableStateOf(null) }
|
|
||||||
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
val onListing: (MangaListing) -> Unit = {
|
|
||||||
mangaListing = it
|
|
||||||
}
|
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
context.settingsDataStore.updateData {
|
context.settingsDataStore.updateData {
|
||||||
@@ -82,13 +105,6 @@ fun Main(navController: NavController) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val onReader: (ReaderInfo) -> Unit = { readerInfo ->
|
|
||||||
coroutineScope.launch {
|
|
||||||
sheetState.snapTo(ModalBottomSheetValue.Hidden)
|
|
||||||
navController.navigate("manatoki.net/reader/${readerInfo.itemID}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var sourceSelectDialog by remember { mutableStateOf(false) }
|
var sourceSelectDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
if (sourceSelectDialog)
|
if (sourceSelectDialog)
|
||||||
@@ -107,11 +123,86 @@ fun Main(navController: NavController) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mangaListing: MangaListing? by rememberSaveable { mutableStateOf(null) }
|
||||||
|
var recentItem: String? by rememberSaveable { mutableStateOf(null) }
|
||||||
|
val mangaListingListState = rememberLazyListState()
|
||||||
|
var mangaListingListSize: Size? by remember { mutableStateOf(null) }
|
||||||
|
val mangaListingInteractionSource = remember { mutableStateMapOf<String, MutableInteractionSource>() }
|
||||||
|
val navigationBarsPadding = LocalDensity.current.run {
|
||||||
|
rememberInsetsPaddingValues(
|
||||||
|
LocalWindowInsets.current.navigationBars
|
||||||
|
).calculateBottomPadding().toPx()
|
||||||
|
}
|
||||||
|
|
||||||
|
val onListing: (MangaListing) -> Unit = {
|
||||||
|
mangaListing = it
|
||||||
|
|
||||||
|
coroutineScope.launch {
|
||||||
|
val recentItemID = historyDao.getAll(it.itemID).firstOrNull() ?: return@launch
|
||||||
|
recentItem = recentItemID
|
||||||
|
|
||||||
|
while (mangaListingListState.layoutInfo.totalItemsCount != it.entries.size) {
|
||||||
|
delay(100)
|
||||||
|
}
|
||||||
|
|
||||||
|
val interactionSource = mangaListingInteractionSource.getOrPut(recentItemID) {
|
||||||
|
MutableInteractionSource()
|
||||||
|
}
|
||||||
|
|
||||||
|
val targetIndex =
|
||||||
|
it.entries.indexOfFirst { entry -> entry.itemID == recentItemID }
|
||||||
|
|
||||||
|
mangaListingListState.scrollToItem(targetIndex)
|
||||||
|
|
||||||
|
mangaListingListSize?.let { sheetSize ->
|
||||||
|
val targetItem =
|
||||||
|
mangaListingListState.layoutInfo.visibleItemsInfo.first {
|
||||||
|
it.key == recentItemID
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetItem.offset == 0) {
|
||||||
|
mangaListingListState.animateScrollBy(
|
||||||
|
-(sheetSize.height - navigationBarsPadding - targetItem.size)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(200)
|
||||||
|
|
||||||
|
with(interactionSource) {
|
||||||
|
val interaction =
|
||||||
|
PressInteraction.Press(
|
||||||
|
Offset(
|
||||||
|
sheetSize.width / 2,
|
||||||
|
targetItem.size / 2f
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
emit(interaction)
|
||||||
|
emit(PressInteraction.Release(interaction))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val onReader: (ReaderInfo) -> Unit = { readerInfo ->
|
||||||
|
coroutineScope.launch {
|
||||||
|
sheetState.snapTo(ModalBottomSheetValue.Hidden)
|
||||||
|
navController.navigate("manatoki.net/reader/${readerInfo.itemID}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ModalBottomSheetLayout(
|
ModalBottomSheetLayout(
|
||||||
sheetState = sheetState,
|
sheetState = sheetState,
|
||||||
sheetShape = RoundedCornerShape(32.dp, 32.dp, 0.dp, 0.dp),
|
sheetShape = RoundedCornerShape(32.dp, 32.dp, 0.dp, 0.dp),
|
||||||
sheetContent = {
|
sheetContent = {
|
||||||
MangaListingBottomSheet(mangaListing) {
|
MangaListingBottomSheet(
|
||||||
|
mangaListing,
|
||||||
|
onListSize = { mangaListingListSize = it },
|
||||||
|
rippleInteractionSource = mangaListingInteractionSource,
|
||||||
|
listState = mangaListingListState,
|
||||||
|
recentItem = recentItem
|
||||||
|
) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
client.getItem(it, onListing, onReader)
|
client.getItem(it, onListing, onReader)
|
||||||
}
|
}
|
||||||
@@ -164,6 +255,50 @@ fun Main(navController: NavController) {
|
|||||||
.verticalScroll(rememberScrollState()),
|
.verticalScroll(rememberScrollState()),
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
|
if (recentManga.isNotEmpty()) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
"이어 보기",
|
||||||
|
style = MaterialTheme.typography.h5
|
||||||
|
)
|
||||||
|
|
||||||
|
IconButton(onClick = { navController.navigate("manatoki.net/recent") }) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Add,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyRow(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(210.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
items(recentManga) { item ->
|
||||||
|
Thumbnail(
|
||||||
|
item,
|
||||||
|
Modifier
|
||||||
|
.width(180.dp)
|
||||||
|
.aspectRatio(6 / 7f)
|
||||||
|
) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
mangaListing = null
|
||||||
|
sheetState.animateTo(ModalBottomSheetValue.Expanded)
|
||||||
|
}
|
||||||
|
coroutineScope.launch {
|
||||||
|
client.getItem(it, onListing, onReader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
@@ -195,7 +330,7 @@ fun Main(navController: NavController) {
|
|||||||
.aspectRatio(6 / 7f)) {
|
.aspectRatio(6 / 7f)) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
mangaListing = null
|
mangaListing = null
|
||||||
sheetState.show()
|
sheetState.animateTo(ModalBottomSheetValue.Expanded)
|
||||||
}
|
}
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
client.getItem(it, onListing, onReader)
|
client.getItem(it, onListing, onReader)
|
||||||
@@ -254,7 +389,7 @@ fun Main(navController: NavController) {
|
|||||||
.aspectRatio(6f / 7)) {
|
.aspectRatio(6f / 7)) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
mangaListing = null
|
mangaListing = null
|
||||||
sheetState.show()
|
sheetState.animateTo(ModalBottomSheetValue.Expanded)
|
||||||
}
|
}
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
client.getItem(it, onListing, onReader)
|
client.getItem(it, onListing, onReader)
|
||||||
@@ -273,7 +408,7 @@ fun Main(navController: NavController) {
|
|||||||
modifier = Modifier.clickable {
|
modifier = Modifier.clickable {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
mangaListing = null
|
mangaListing = null
|
||||||
sheetState.show()
|
sheetState.animateTo(ModalBottomSheetValue.Expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
|
|||||||
@@ -18,14 +18,16 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil.sources.manatoki.composable
|
package xyz.quaver.pupil.sources.manatoki.composable
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.indication
|
import androidx.compose.foundation.indication
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.*
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
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.ArrowRight
|
import androidx.compose.material.icons.filled.ArrowRight
|
||||||
@@ -39,7 +41,6 @@ import androidx.compose.ui.geometry.Size
|
|||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.SubcomposeLayout
|
import androidx.compose.ui.layout.SubcomposeLayout
|
||||||
import androidx.compose.ui.layout.onGloballyPositioned
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
import androidx.compose.ui.layout.positionInWindow
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.toSize
|
import androidx.compose.ui.unit.toSize
|
||||||
import coil.compose.rememberImagePainter
|
import coil.compose.rememberImagePainter
|
||||||
@@ -50,7 +51,7 @@ import com.google.accompanist.insets.rememberInsetsPaddingValues
|
|||||||
import xyz.quaver.pupil.sources.manatoki.MangaListing
|
import xyz.quaver.pupil.sources.manatoki.MangaListing
|
||||||
|
|
||||||
private val FabSpacing = 8.dp
|
private val FabSpacing = 8.dp
|
||||||
private val HeightPercentage = 75 // take 60% of the available space
|
private val HeightPercentage = 75 // take 75% of the available space
|
||||||
private enum class MangaListingBottomSheetLayoutContent { Top, Bottom, Fab }
|
private enum class MangaListingBottomSheetLayoutContent { Top, Bottom, Fab }
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -107,7 +108,9 @@ fun MangaListingBottomSheet(
|
|||||||
currentItemID: String? = null,
|
currentItemID: String? = null,
|
||||||
onListSize: (Size) -> Unit = { },
|
onListSize: (Size) -> Unit = { },
|
||||||
listState: LazyListState = rememberLazyListState(),
|
listState: LazyListState = rememberLazyListState(),
|
||||||
rippleInteractionSource: List<MutableInteractionSource> = emptyList(),
|
rippleInteractionSource: Map<String, MutableInteractionSource> = emptyMap(),
|
||||||
|
recentItem: String? = null,
|
||||||
|
nextItem: String? = null,
|
||||||
onOpenItem: (String) -> Unit = { },
|
onOpenItem: (String) -> Unit = { },
|
||||||
) {
|
) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
@@ -125,9 +128,19 @@ fun MangaListingBottomSheet(
|
|||||||
MangaListingBottomSheetLayout(
|
MangaListingBottomSheetLayout(
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
ExtendedFloatingActionButton(
|
ExtendedFloatingActionButton(
|
||||||
text = { Text("첫화보기") },
|
text = { Text(
|
||||||
|
when {
|
||||||
|
mangaListing.entries.any { it.itemID == recentItem } -> "이어보기"
|
||||||
|
mangaListing.entries.any { it.itemID == nextItem } -> "다음화보기"
|
||||||
|
else -> "첫화보기"
|
||||||
|
}
|
||||||
|
) },
|
||||||
onClick = {
|
onClick = {
|
||||||
mangaListing.entries.lastOrNull()?.let { onOpenItem(it.itemID) }
|
when {
|
||||||
|
mangaListing.entries.any { it.itemID == recentItem } -> onOpenItem(recentItem!!)
|
||||||
|
mangaListing.entries.any { it.itemID == nextItem } -> onOpenItem(nextItem!!)
|
||||||
|
else -> mangaListing.entries.lastOrNull()?.let { onOpenItem(it.itemID) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -216,9 +229,7 @@ fun MangaListingBottomSheet(
|
|||||||
onOpenItem(entry.itemID)
|
onOpenItem(entry.itemID)
|
||||||
}
|
}
|
||||||
.run {
|
.run {
|
||||||
rippleInteractionSource
|
rippleInteractionSource[entry.itemID]?.let {
|
||||||
.getOrNull(index)
|
|
||||||
?.let {
|
|
||||||
indication(it, rememberRipple())
|
indication(it, rememberRipple())
|
||||||
} ?: this
|
} ?: this
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +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.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
@@ -82,6 +83,7 @@ fun Reader(navController: NavController) {
|
|||||||
val database: ManatokiDatabase by rememberInstance()
|
val database: ManatokiDatabase by rememberInstance()
|
||||||
val favoriteDao = remember { database.favoriteDao() }
|
val favoriteDao = remember { database.favoriteDao() }
|
||||||
val bookmarkDao = remember { database.bookmarkDao() }
|
val bookmarkDao = remember { database.bookmarkDao() }
|
||||||
|
val historyDao = remember { database.historyDao() }
|
||||||
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
@@ -91,6 +93,9 @@ fun Reader(navController: NavController) {
|
|||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
if (itemID != null)
|
if (itemID != null)
|
||||||
client.getItem(itemID, onReader = {
|
client.getItem(itemID, onReader = {
|
||||||
|
coroutineScope.launch {
|
||||||
|
historyDao.insert(it.itemID, it.listingItemID, 1)
|
||||||
|
}
|
||||||
readerInfo = it
|
readerInfo = it
|
||||||
model.load(it.urls) {
|
model.load(it.urls) {
|
||||||
set("User-Agent", imageUserAgent)
|
set("User-Agent", imageUserAgent)
|
||||||
@@ -102,17 +107,24 @@ fun Reader(navController: NavController) {
|
|||||||
val isFavorite by favoriteDao.contains(itemID ?: "").collectAsState(false)
|
val isFavorite by favoriteDao.contains(itemID ?: "").collectAsState(false)
|
||||||
|
|
||||||
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
|
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
|
||||||
var mangaListing: MangaListing? by rememberSaveable { mutableStateOf(null) }
|
|
||||||
val mangaListingRippleInteractionSource = remember { mutableStateListOf<MutableInteractionSource>() }
|
|
||||||
val navigationBarsPadding = LocalDensity.current.run {
|
val navigationBarsPadding = LocalDensity.current.run {
|
||||||
rememberInsetsPaddingValues(
|
rememberInsetsPaddingValues(
|
||||||
LocalWindowInsets.current.navigationBars
|
LocalWindowInsets.current.navigationBars
|
||||||
).calculateBottomPadding().toPx()
|
).calculateBottomPadding().toPx()
|
||||||
}
|
}
|
||||||
|
|
||||||
val bottomSheetListState = rememberLazyListState()
|
|
||||||
val readerListState = rememberLazyListState()
|
val readerListState = rememberLazyListState()
|
||||||
|
|
||||||
|
LaunchedEffect(readerListState.firstVisibleItemIndex) {
|
||||||
|
readerInfo?.let { readerInfo ->
|
||||||
|
historyDao.insert(
|
||||||
|
readerInfo.itemID,
|
||||||
|
readerInfo.listingItemID,
|
||||||
|
readerListState.firstVisibleItemIndex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var scrollDirection by remember { mutableStateOf(0f) }
|
var scrollDirection by remember { mutableStateOf(0f) }
|
||||||
|
|
||||||
BackHandler {
|
BackHandler {
|
||||||
@@ -123,7 +135,10 @@ fun Reader(navController: NavController) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mangaListing: MangaListing? by rememberSaveable { mutableStateOf(null) }
|
||||||
|
val mangaListingListState = rememberLazyListState()
|
||||||
var mangaListingListSize: Size? by remember { mutableStateOf(null) }
|
var mangaListingListSize: Size? by remember { mutableStateOf(null) }
|
||||||
|
val mangaListingRippleInteractionSource = remember { MutableInteractionSource() }
|
||||||
|
|
||||||
ModalBottomSheetLayout(
|
ModalBottomSheetLayout(
|
||||||
sheetState = sheetState,
|
sheetState = sheetState,
|
||||||
@@ -132,11 +147,10 @@ fun Reader(navController: NavController) {
|
|||||||
MangaListingBottomSheet(
|
MangaListingBottomSheet(
|
||||||
mangaListing,
|
mangaListing,
|
||||||
currentItemID = itemID,
|
currentItemID = itemID,
|
||||||
onListSize = {
|
onListSize = { mangaListingListSize = it },
|
||||||
mangaListingListSize = it
|
rippleInteractionSource = if (itemID == null) emptyMap() else mapOf(itemID to mangaListingRippleInteractionSource),
|
||||||
},
|
listState = mangaListingListState,
|
||||||
rippleInteractionSource = mangaListingRippleInteractionSource,
|
nextItem = readerInfo?.nextItemID
|
||||||
listState = bottomSheetListState
|
|
||||||
) {
|
) {
|
||||||
navController.navigate("manatoki.net/reader/$it") {
|
navController.navigate("manatoki.net/reader/$it") {
|
||||||
popUpTo("manatoki.net/")
|
popUpTo("manatoki.net/")
|
||||||
@@ -214,7 +228,7 @@ fun Reader(navController: NavController) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
sheetState.show()
|
sheetState.animateTo(ModalBottomSheetValue.Expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
@@ -222,42 +236,31 @@ fun Reader(navController: NavController) {
|
|||||||
client.getItem(it.listingItemID, onListing = {
|
client.getItem(it.listingItemID, onListing = {
|
||||||
mangaListing = it
|
mangaListing = it
|
||||||
|
|
||||||
mangaListingRippleInteractionSource.addAll(
|
|
||||||
List(
|
|
||||||
max(
|
|
||||||
it.entries.size - mangaListingRippleInteractionSource.size,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
MutableInteractionSource()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
while (bottomSheetListState.layoutInfo.totalItemsCount != it.entries.size) {
|
while (mangaListingListState.layoutInfo.totalItemsCount != it.entries.size) {
|
||||||
delay(100)
|
delay(100)
|
||||||
}
|
}
|
||||||
|
|
||||||
val targetIndex =
|
val targetIndex =
|
||||||
it.entries.indexOfFirst { it.itemID == itemID }
|
it.entries.indexOfFirst { it.itemID == itemID }
|
||||||
|
|
||||||
bottomSheetListState.scrollToItem(targetIndex)
|
mangaListingListState.scrollToItem(targetIndex)
|
||||||
|
|
||||||
mangaListingListSize?.let { sheetSize ->
|
mangaListingListSize?.let { sheetSize ->
|
||||||
val targetItem =
|
val targetItem =
|
||||||
bottomSheetListState.layoutInfo.visibleItemsInfo.first {
|
mangaListingListState.layoutInfo.visibleItemsInfo.first {
|
||||||
it.key == itemID
|
it.key == itemID
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetItem.offset == 0) {
|
if (targetItem.offset == 0) {
|
||||||
bottomSheetListState.animateScrollBy(
|
mangaListingListState.animateScrollBy(
|
||||||
-(sheetSize.height - navigationBarsPadding - targetItem.size)
|
-(sheetSize.height - navigationBarsPadding - targetItem.size)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
delay(200)
|
delay(200)
|
||||||
|
|
||||||
with(mangaListingRippleInteractionSource[targetIndex]) {
|
with(mangaListingRippleInteractionSource) {
|
||||||
val interaction =
|
val interaction =
|
||||||
PressInteraction.Press(
|
PressInteraction.Press(
|
||||||
Offset(
|
Offset(
|
||||||
|
|||||||
@@ -24,16 +24,15 @@ import androidx.compose.foundation.layout.*
|
|||||||
import androidx.compose.foundation.lazy.GridCells
|
import androidx.compose.foundation.lazy.GridCells
|
||||||
import androidx.compose.foundation.lazy.LazyVerticalGrid
|
import androidx.compose.foundation.lazy.LazyVerticalGrid
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
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.NavigateBefore
|
import androidx.compose.material.icons.filled.NavigateBefore
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import com.google.accompanist.insets.LocalWindowInsets
|
import com.google.accompanist.insets.LocalWindowInsets
|
||||||
import com.google.accompanist.insets.rememberInsetsPaddingValues
|
import com.google.accompanist.insets.rememberInsetsPaddingValues
|
||||||
@@ -44,7 +43,6 @@ import kotlinx.coroutines.launch
|
|||||||
import org.kodein.di.compose.rememberInstance
|
import org.kodein.di.compose.rememberInstance
|
||||||
import org.kodein.di.compose.rememberViewModel
|
import org.kodein.di.compose.rememberViewModel
|
||||||
import xyz.quaver.pupil.sources.composable.OverscrollPager
|
import xyz.quaver.pupil.sources.composable.OverscrollPager
|
||||||
import xyz.quaver.pupil.sources.manatoki.MangaListing
|
|
||||||
import xyz.quaver.pupil.sources.manatoki.getItem
|
import xyz.quaver.pupil.sources.manatoki.getItem
|
||||||
import xyz.quaver.pupil.sources.manatoki.viewmodel.RecentViewModel
|
import xyz.quaver.pupil.sources.manatoki.viewmodel.RecentViewModel
|
||||||
|
|
||||||
@@ -58,34 +56,14 @@ fun Recent(navController: NavController) {
|
|||||||
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
var mangaListing: MangaListing? by rememberSaveable { mutableStateOf(null) }
|
|
||||||
val state = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
model.load()
|
model.load()
|
||||||
}
|
}
|
||||||
|
|
||||||
BackHandler {
|
BackHandler {
|
||||||
if (state.isVisible) coroutineScope.launch { state.hide() }
|
navController.popBackStack()
|
||||||
else navController.popBackStack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ModalBottomSheetLayout(
|
|
||||||
sheetState = state,
|
|
||||||
sheetShape = RoundedCornerShape(32.dp, 32.dp, 0.dp, 0.dp),
|
|
||||||
sheetContent = {
|
|
||||||
MangaListingBottomSheet(mangaListing) {
|
|
||||||
coroutineScope.launch {
|
|
||||||
client.getItem(it, onReader = {
|
|
||||||
launch {
|
|
||||||
state.snapTo(ModalBottomSheetValue.Hidden)
|
|
||||||
navController.navigate("manatoki.net/reader/${it.itemID}")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
@@ -136,12 +114,8 @@ fun Recent(navController: NavController) {
|
|||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
) {
|
) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
mangaListing = null
|
client.getItem(it, onReader = {
|
||||||
state.show()
|
navController.navigate("manatoki.net/reader/${it.itemID}")
|
||||||
}
|
|
||||||
coroutineScope.launch {
|
|
||||||
client.getItem(it, onListing = {
|
|
||||||
mangaListing = it
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,5 +128,4 @@ fun Recent(navController: NavController) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ import android.util.Log
|
|||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.core.animateDpAsState
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.gestures.animateScrollBy
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.interaction.PressInteraction
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.GridCells
|
import androidx.compose.foundation.lazy.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.LazyVerticalGrid
|
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
@@ -42,8 +42,11 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.onFocusChanged
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.geometry.Size
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -56,15 +59,14 @@ import com.google.accompanist.insets.rememberInsetsPaddingValues
|
|||||||
import com.google.accompanist.insets.ui.Scaffold
|
import com.google.accompanist.insets.ui.Scaffold
|
||||||
import com.google.accompanist.insets.ui.TopAppBar
|
import com.google.accompanist.insets.ui.TopAppBar
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.kodein.di.compose.rememberInstance
|
import org.kodein.di.compose.rememberInstance
|
||||||
import org.kodein.di.compose.rememberViewModel
|
import org.kodein.di.compose.rememberViewModel
|
||||||
import xyz.quaver.pupil.sources.composable.ModalTopSheetLayout
|
import xyz.quaver.pupil.sources.composable.ModalTopSheetLayout
|
||||||
import xyz.quaver.pupil.sources.composable.ModalTopSheetState
|
import xyz.quaver.pupil.sources.composable.ModalTopSheetState
|
||||||
import xyz.quaver.pupil.sources.composable.OverscrollPager
|
import xyz.quaver.pupil.sources.composable.OverscrollPager
|
||||||
import xyz.quaver.pupil.sources.manatoki.Chip
|
import xyz.quaver.pupil.sources.manatoki.*
|
||||||
import xyz.quaver.pupil.sources.manatoki.MangaListing
|
|
||||||
import xyz.quaver.pupil.sources.manatoki.getItem
|
|
||||||
import xyz.quaver.pupil.sources.manatoki.viewmodel.*
|
import xyz.quaver.pupil.sources.manatoki.viewmodel.*
|
||||||
|
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
@@ -75,14 +77,15 @@ fun Search(navController: NavController) {
|
|||||||
|
|
||||||
val client: HttpClient by rememberInstance()
|
val client: HttpClient by rememberInstance()
|
||||||
|
|
||||||
|
val database: ManatokiDatabase by rememberInstance()
|
||||||
|
val historyDao = remember { database.historyDao() }
|
||||||
|
|
||||||
var searchFocused by remember { mutableStateOf(false) }
|
var searchFocused by remember { mutableStateOf(false) }
|
||||||
val handleOffset by animateDpAsState(if (searchFocused) 0.dp else (-36).dp)
|
val handleOffset by animateDpAsState(if (searchFocused) 0.dp else (-36).dp)
|
||||||
|
|
||||||
val drawerState = rememberSwipeableState(ModalTopSheetState.Hidden)
|
val drawerState = rememberSwipeableState(ModalTopSheetState.Hidden)
|
||||||
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
|
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
|
||||||
|
|
||||||
var mangaListing: MangaListing? by rememberSaveable { mutableStateOf(null) }
|
|
||||||
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
val focusManager = LocalFocusManager.current
|
val focusManager = LocalFocusManager.current
|
||||||
@@ -100,11 +103,28 @@ fun Search(navController: NavController) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mangaListing: MangaListing? by rememberSaveable { mutableStateOf(null) }
|
||||||
|
var recentItem: String? by rememberSaveable { mutableStateOf(null) }
|
||||||
|
val mangaListingListState = rememberLazyListState()
|
||||||
|
var mangaListingListSize: Size? by remember { mutableStateOf(null) }
|
||||||
|
val mangaListingInteractionSource = remember { mutableStateMapOf<String, MutableInteractionSource>() }
|
||||||
|
val navigationBarsPadding = LocalDensity.current.run {
|
||||||
|
rememberInsetsPaddingValues(
|
||||||
|
LocalWindowInsets.current.navigationBars
|
||||||
|
).calculateBottomPadding().toPx()
|
||||||
|
}
|
||||||
|
|
||||||
ModalBottomSheetLayout(
|
ModalBottomSheetLayout(
|
||||||
sheetState = sheetState,
|
sheetState = sheetState,
|
||||||
sheetShape = RoundedCornerShape(32.dp, 32.dp, 0.dp, 0.dp),
|
sheetShape = RoundedCornerShape(32.dp, 32.dp, 0.dp, 0.dp),
|
||||||
sheetContent = {
|
sheetContent = {
|
||||||
MangaListingBottomSheet(mangaListing) {
|
MangaListingBottomSheet(
|
||||||
|
mangaListing,
|
||||||
|
onListSize = { mangaListingListSize = it },
|
||||||
|
rippleInteractionSource = mangaListingInteractionSource,
|
||||||
|
listState = mangaListingListState,
|
||||||
|
recentItem = recentItem
|
||||||
|
) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
client.getItem(it, onReader = {
|
client.getItem(it, onReader = {
|
||||||
launch {
|
launch {
|
||||||
@@ -201,17 +221,6 @@ fun Search(navController: NavController) {
|
|||||||
.verticalScroll(rememberScrollState()),
|
.verticalScroll(rememberScrollState()),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
var expanded by remember { mutableStateOf(false) }
|
|
||||||
val suggestedArtists = remember(model.artist) {
|
|
||||||
if (model.artist.isEmpty())
|
|
||||||
model.availableArtists
|
|
||||||
else
|
|
||||||
model
|
|
||||||
.availableArtists
|
|
||||||
.filter { it.contains(model.artist) }
|
|
||||||
.sortedBy { if (it.startsWith(model.artist)) 0 else 1 }
|
|
||||||
}.take(20)
|
|
||||||
|
|
||||||
Text("작가")
|
Text("작가")
|
||||||
TextField(model.artist, onValueChange = { model.artist = it })
|
TextField(model.artist, onValueChange = { model.artist = it })
|
||||||
|
|
||||||
@@ -266,7 +275,10 @@ fun Search(navController: NavController) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Box(Modifier.fillMaxWidth().height(8.dp))
|
Box(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(8.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -301,11 +313,58 @@ fun Search(navController: NavController) {
|
|||||||
) {
|
) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
mangaListing = null
|
mangaListing = null
|
||||||
sheetState.show()
|
sheetState.animateTo(ModalBottomSheetValue.Expanded)
|
||||||
}
|
}
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
client.getItem(it, onListing = {
|
client.getItem(it, onListing = {
|
||||||
mangaListing = it
|
mangaListing = it
|
||||||
|
|
||||||
|
coroutineScope.launch {
|
||||||
|
val recentItemID = historyDao.getAll(it.itemID).firstOrNull() ?: return@launch
|
||||||
|
recentItem = recentItemID
|
||||||
|
|
||||||
|
while (mangaListingListState.layoutInfo.totalItemsCount != it.entries.size) {
|
||||||
|
delay(100)
|
||||||
|
}
|
||||||
|
|
||||||
|
val interactionSource = mangaListingInteractionSource.getOrPut(recentItemID) {
|
||||||
|
MutableInteractionSource()
|
||||||
|
}
|
||||||
|
|
||||||
|
val targetIndex =
|
||||||
|
it.entries.indexOfFirst { entry -> entry.itemID == recentItemID }
|
||||||
|
|
||||||
|
mangaListingListState.scrollToItem(targetIndex)
|
||||||
|
|
||||||
|
mangaListingListSize?.let { sheetSize ->
|
||||||
|
val targetItem =
|
||||||
|
mangaListingListState.layoutInfo.visibleItemsInfo.first {
|
||||||
|
it.key == recentItemID
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetItem.offset == 0) {
|
||||||
|
mangaListingListState.animateScrollBy(
|
||||||
|
-(sheetSize.height - navigationBarsPadding - targetItem.size)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(200)
|
||||||
|
|
||||||
|
with(interactionSource) {
|
||||||
|
val interaction =
|
||||||
|
PressInteraction.Press(
|
||||||
|
Offset(
|
||||||
|
sheetSize.width / 2,
|
||||||
|
targetItem.size / 2f
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
emit(interaction)
|
||||||
|
emit(PressInteraction.Release(interaction))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ fun Chip(text: String, selected: Boolean = false, onClick: () -> Unit = { }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val cache = LruCache<String, Any>(50)
|
private val cache = LruCache<String, Any>(100)
|
||||||
suspend fun HttpClient.getItem(
|
suspend fun HttpClient.getItem(
|
||||||
itemID: String,
|
itemID: String,
|
||||||
onListing: (MangaListing) -> Unit = { },
|
onListing: (MangaListing) -> Unit = { },
|
||||||
@@ -144,7 +144,7 @@ suspend fun HttpClient.getItem(
|
|||||||
}.toString()
|
}.toString()
|
||||||
|
|
||||||
val urls = Jsoup.parse(htmlData)
|
val urls = Jsoup.parse(htmlData)
|
||||||
.select("img[^data-]:not([style]):not([^class])")
|
.select("img[^data-]:not([style])")
|
||||||
.map {
|
.map {
|
||||||
it.attributes()
|
it.attributes()
|
||||||
.first { it.key.startsWith("data-") }
|
.first { it.key.startsWith("data-") }
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
package xyz.quaver.pupil.sources.manatoki.viewmodel
|
package xyz.quaver.pupil.sources.manatoki.viewmodel
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
@@ -36,6 +37,7 @@ import org.kodein.di.android.closestDI
|
|||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
import org.kodein.log.LoggerFactory
|
import org.kodein.log.LoggerFactory
|
||||||
import org.kodein.log.newLogger
|
import org.kodein.log.newLogger
|
||||||
|
import xyz.quaver.pupil.sources.manatoki.HistoryDao
|
||||||
import xyz.quaver.pupil.sources.manatoki.composable.Thumbnail
|
import xyz.quaver.pupil.sources.manatoki.composable.Thumbnail
|
||||||
import xyz.quaver.pupil.sources.manatoki.manatokiUrl
|
import xyz.quaver.pupil.sources.manatoki.manatokiUrl
|
||||||
import xyz.quaver.pupil.sources.manatoki.waitForRateLimit
|
import xyz.quaver.pupil.sources.manatoki.waitForRateLimit
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const val GROUP_ID = "xyz.quaver"
|
const val GROUP_ID = "xyz.quaver"
|
||||||
const val VERSION = "6.0.0-alpha01"
|
const val VERSION = "6.0.0-alpha02"
|
||||||
|
|
||||||
object Versions {
|
object Versions {
|
||||||
const val KOTLIN_VERSION = "1.5.31"
|
const val KOTLIN_VERSION = "1.5.31"
|
||||||
|
|||||||
Reference in New Issue
Block a user