Insets
Nested navigation Hitomi main actions
This commit is contained in:
2
.idea/deploymentTargetDropDown.xml
generated
2
.idea/deploymentTargetDropDown.xml
generated
@@ -12,6 +12,6 @@
|
||||
</deviceKey>
|
||||
</Target>
|
||||
</runningDeviceTargetSelectedWithDropDown>
|
||||
<timeTargetWasSelectedWithDropDown value="2021-12-18T14:48:54.587703Z" />
|
||||
<timeTargetWasSelectedWithDropDown value="2021-12-19T03:31:58.153375Z" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -19,8 +19,8 @@
|
||||
package xyz.quaver.pupil.sources
|
||||
|
||||
import android.app.Application
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import org.kodein.di.*
|
||||
import xyz.quaver.pupil.sources.hitomi.Hitomi
|
||||
|
||||
@@ -28,14 +28,7 @@ abstract class Source {
|
||||
abstract val name: String
|
||||
abstract val iconResID: Int
|
||||
|
||||
@Composable
|
||||
open fun MainScreen(navController: NavController) { }
|
||||
|
||||
@Composable
|
||||
open fun Search(navController: NavController) { }
|
||||
|
||||
@Composable
|
||||
open fun Reader(navController: NavController) { }
|
||||
open fun NavGraphBuilder.navGraph(navController: NavController) { }
|
||||
}
|
||||
|
||||
typealias SourceEntry = Pair<String, Source>
|
||||
|
||||
@@ -24,10 +24,7 @@ import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.runtime.*
|
||||
@@ -80,7 +77,8 @@ fun FloatingSearchBar(
|
||||
elevation = 8.dp
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxSize().padding(16.dp, 0.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
navigationIcon()
|
||||
@@ -91,14 +89,15 @@ fun FloatingSearchBar(
|
||||
.padding(16.dp, 0.dp)
|
||||
.onFocusChanged {
|
||||
if (it.isFocused) onTextFieldFocused()
|
||||
else onTextFieldUnfocused()
|
||||
else onTextFieldUnfocused()
|
||||
|
||||
isFocused = it.isFocused
|
||||
},
|
||||
value = query,
|
||||
onValueChange = onQueryChange,
|
||||
singleLine = true,
|
||||
cursorBrush = SolidColor(MaterialTheme.colors.primary),
|
||||
textStyle = LocalTextStyle.current.copy(color = MaterialTheme.colors.onSurface),
|
||||
cursorBrush = SolidColor(MaterialTheme.colors.secondary),
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
|
||||
keyboardActions = KeyboardActions(
|
||||
onSearch = {
|
||||
@@ -129,11 +128,14 @@ fun FloatingSearchBar(
|
||||
}
|
||||
)
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(24.dp),
|
||||
content = actions
|
||||
)
|
||||
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
|
||||
Row(
|
||||
Modifier.fillMaxHeight(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
content = actions
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,10 +28,10 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun <T> ListSearchResult(searchResults: List<T>, content: @Composable (T) -> Unit) {
|
||||
fun <T> ListSearchResult(searchResults: List<T>, contentPadding: PaddingValues = PaddingValues(0.dp), content: @Composable (T) -> Unit) {
|
||||
LazyColumn(
|
||||
Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(0.dp, 64.dp, 0.dp, 0.dp),
|
||||
contentPadding = contentPadding,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
items(searchResults) { itemInfo ->
|
||||
|
||||
@@ -126,6 +126,7 @@ private class FloatingActionButtonItemProvider : PreviewParameterProvider<SubFab
|
||||
@Composable
|
||||
fun MultipleFloatingActionButton(
|
||||
@PreviewParameter(provider = FloatingActionButtonItemProvider::class) items: List<SubFabItem>,
|
||||
modifier: Modifier = Modifier,
|
||||
fabIcon: ImageVector = Icons.Default.Add,
|
||||
visible: Boolean = true,
|
||||
targetState: FloatingActionButtonState = FloatingActionButtonState.COLLAPSED,
|
||||
@@ -150,6 +151,7 @@ fun MultipleFloatingActionButton(
|
||||
if (!visible) onStateChanged?.invoke(FloatingActionButtonState.COLLAPSED)
|
||||
|
||||
Column(
|
||||
modifier = modifier,
|
||||
horizontalAlignment = Alignment.End,
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
|
||||
@@ -41,6 +41,11 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.google.accompanist.insets.LocalWindowInsets
|
||||
import com.google.accompanist.insets.rememberInsetsPaddingValues
|
||||
import com.google.accompanist.insets.ui.Scaffold
|
||||
import com.google.accompanist.insets.ui.TopAppBar
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.http.*
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -172,6 +177,16 @@ fun ReaderBase(
|
||||
}
|
||||
}
|
||||
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val useDarkIcons = MaterialTheme.colors.isLight
|
||||
|
||||
SideEffect {
|
||||
systemUiController.setSystemBarsColor(
|
||||
color = Color.Transparent,
|
||||
darkIcons = useDarkIcons
|
||||
)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
if (!model.isFullscreen)
|
||||
@@ -186,7 +201,11 @@ fun ReaderBase(
|
||||
},
|
||||
actions = {
|
||||
//TODO
|
||||
}
|
||||
},
|
||||
contentPadding = rememberInsetsPaddingValues(
|
||||
LocalWindowInsets.current.statusBars,
|
||||
applyBottom = false
|
||||
)
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
@@ -208,8 +227,8 @@ fun ReaderBase(
|
||||
},
|
||||
scaffoldState = scaffoldState,
|
||||
snackbarHost = { scaffoldState.snackbarHostState }
|
||||
) {
|
||||
Box {
|
||||
) { contentPadding ->
|
||||
Box(Modifier.padding(contentPadding)) {
|
||||
LazyColumn(
|
||||
Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
|
||||
@@ -24,20 +24,18 @@ import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.awaitFirstDown
|
||||
import androidx.compose.foundation.gestures.forEachGesture
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.NavigateBefore
|
||||
import androidx.compose.material.icons.filled.NavigateNext
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
||||
@@ -55,6 +53,12 @@ import androidx.compose.ui.util.fastFirstOrNull
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.google.accompanist.drawablepainter.rememberDrawablePainter
|
||||
import com.google.accompanist.insets.LocalWindowInsets
|
||||
import com.google.accompanist.insets.navigationBarsPadding
|
||||
import com.google.accompanist.insets.rememberInsetsPaddingValues
|
||||
import com.google.accompanist.insets.systemBarsPadding
|
||||
import com.google.accompanist.insets.ui.Scaffold
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.ui.theme.LightBlue300
|
||||
import kotlin.math.*
|
||||
@@ -95,7 +99,7 @@ fun <T> SearchBase(
|
||||
fabSubMenu: List<SubFabItem> = emptyList(),
|
||||
actions: @Composable RowScope.() -> Unit = { },
|
||||
onSearch: () -> Unit = { },
|
||||
content: @Composable BoxScope.() -> Unit
|
||||
content: @Composable BoxScope.(contentPadding: PaddingValues) -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
@@ -115,11 +119,25 @@ fun <T> SearchBase(
|
||||
}
|
||||
}
|
||||
|
||||
val systemBarsPaddingValues = rememberInsetsPaddingValues(insets = LocalWindowInsets.current.systemBars)
|
||||
|
||||
val pageTurnIndicatorHeight = LocalDensity.current.run { 64.dp.toPx() }
|
||||
val searchBarHeight = LocalDensity.current.run { 64.dp.roundToPx() }
|
||||
|
||||
val searchBarDefaultOffset = systemBarsPaddingValues.calculateTopPadding() + 64.dp
|
||||
val searchBarDefaultOffsetPx = LocalDensity.current.run { searchBarDefaultOffset.roundToPx() }
|
||||
|
||||
var overscroll: Float? by remember { mutableStateOf(null) }
|
||||
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val useDarkIcons = MaterialTheme.colors.isLight
|
||||
|
||||
SideEffect {
|
||||
systemUiController.setSystemBarsColor(
|
||||
color = Color.Transparent,
|
||||
darkIcons = useDarkIcons
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(navigationIconProgress) {
|
||||
navigationIcon.progress = navigationIconProgress
|
||||
}
|
||||
@@ -127,6 +145,7 @@ fun <T> SearchBase(
|
||||
Scaffold(
|
||||
floatingActionButton = {
|
||||
MultipleFloatingActionButton(
|
||||
modifier = Modifier.navigationBarsPadding(),
|
||||
items = fabSubMenu,
|
||||
visible = model.isFabVisible,
|
||||
targetState = isFabExpanded,
|
||||
@@ -135,8 +154,8 @@ fun <T> SearchBase(
|
||||
}
|
||||
)
|
||||
}
|
||||
) {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
) { contentPadding ->
|
||||
Box(Modifier.padding(contentPadding)) {
|
||||
val topCircleRadius by animateFloatAsState(if (overscroll?.let { it >= pageTurnIndicatorHeight } == true) 1000f else 0f)
|
||||
val bottomCircleRadius by animateFloatAsState(if (overscroll?.let { it <= -pageTurnIndicatorHeight } == true) 1000f else 0f)
|
||||
|
||||
@@ -144,7 +163,7 @@ fun <T> SearchBase(
|
||||
Canvas(modifier = Modifier.fillMaxSize()) {
|
||||
drawCircle(
|
||||
LightBlue300.copy(alpha = 0.6f),
|
||||
center = Offset(this.center.x, searchBarHeight.toFloat()),
|
||||
center = Offset(this.center.x, searchBarDefaultOffsetPx.toFloat()),
|
||||
radius = topCircleRadius
|
||||
)
|
||||
drawCircle(
|
||||
@@ -195,7 +214,9 @@ fun <T> SearchBase(
|
||||
modifier = Modifier
|
||||
.offset(
|
||||
0.dp,
|
||||
overscroll?.coerceIn(-pageTurnIndicatorHeight, pageTurnIndicatorHeight)?.let { overscroll -> LocalDensity.current.run { overscroll.toDp() } }
|
||||
overscroll
|
||||
?.coerceIn(-pageTurnIndicatorHeight, pageTurnIndicatorHeight)
|
||||
?.let { overscroll -> LocalDensity.current.run { overscroll.toDp() } }
|
||||
?: 0.dp)
|
||||
.nestedScroll(object : NestedScrollConnection {
|
||||
override fun onPreScroll(
|
||||
@@ -207,7 +228,7 @@ fun <T> SearchBase(
|
||||
if (overscrollSnapshot == null || overscrollSnapshot == 0f) {
|
||||
model.searchBarOffset =
|
||||
(model.searchBarOffset + available.y.roundToInt()).coerceIn(
|
||||
-searchBarHeight,
|
||||
-searchBarDefaultOffsetPx,
|
||||
0
|
||||
)
|
||||
|
||||
@@ -275,29 +296,29 @@ fun <T> SearchBase(
|
||||
}
|
||||
}
|
||||
},
|
||||
content = content
|
||||
content = {
|
||||
this.content(
|
||||
PaddingValues(0.dp, searchBarDefaultOffset, 0.dp, 0.dp)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
if (model.loading)
|
||||
CircularProgressIndicator(Modifier.align(Alignment.Center))
|
||||
|
||||
FloatingSearchBar(
|
||||
modifier = Modifier.offset(0.dp, LocalDensity.current.run { model.searchBarOffset.toDp() }),
|
||||
modifier = Modifier
|
||||
.systemBarsPadding()
|
||||
.offset(0.dp, LocalDensity.current.run { model.searchBarOffset.toDp() }),
|
||||
query = model.query,
|
||||
onQueryChange = { model.query = it },
|
||||
navigationIcon = {
|
||||
Icon(
|
||||
painter = rememberDrawablePainter(navigationIcon),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = rememberRipple(bounded = false)
|
||||
) {
|
||||
focusManager.clearFocus()
|
||||
}
|
||||
)
|
||||
IconButton(onClick = { focusManager.clearFocus() }) {
|
||||
Icon(
|
||||
painter = rememberDrawablePainter(navigationIcon),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = actions,
|
||||
onTextFieldFocused = { navigationIconState = NavigationIconState.ARROW },
|
||||
|
||||
@@ -19,14 +19,27 @@
|
||||
package xyz.quaver.pupil.sources.hitomi
|
||||
|
||||
import android.app.Application
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.filled.Shuffle
|
||||
import androidx.compose.material.icons.filled.Sort
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.navigation
|
||||
import io.ktor.client.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -44,6 +57,7 @@ import xyz.quaver.pupil.sources.hitomi.composable.DetailedSearchResult
|
||||
import xyz.quaver.pupil.sources.hitomi.lib.getGalleryInfo
|
||||
import xyz.quaver.pupil.sources.hitomi.lib.getReferer
|
||||
import xyz.quaver.pupil.sources.hitomi.lib.imageUrlFromImage
|
||||
import xyz.quaver.pupil.ui.dialog.SourceSelectDialog
|
||||
|
||||
class Hitomi(app: Application) : Source(), DIAware {
|
||||
override val di by closestDI(app)
|
||||
@@ -58,16 +72,15 @@ class Hitomi(app: Application) : Source(), DIAware {
|
||||
override val name: String = "hitomi.la"
|
||||
override val iconResID: Int = R.drawable.hitomi
|
||||
|
||||
@Composable
|
||||
override fun MainScreen(navController: NavController) {
|
||||
navController.navigate("search/hitomi.la") {
|
||||
launchSingleTop = true
|
||||
navController.popBackStack()
|
||||
override fun NavGraphBuilder.navGraph(navController: NavController) {
|
||||
navigation(startDestination = "search", route = name) {
|
||||
composable("search") { Search(navController) }
|
||||
composable("reader/{itemID}") { Reader(navController) }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Search(navController: NavController) {
|
||||
fun Search(navController: NavController) {
|
||||
val model: HitomiSearchResultViewModel = viewModel()
|
||||
val database: AppDatabase by rememberInstance()
|
||||
val bookmarkDao = remember { database.bookmarkDao() }
|
||||
@@ -78,7 +91,21 @@ class Hitomi(app: Application) : Source(), DIAware {
|
||||
bookmarks?.toSet() ?: emptySet()
|
||||
}
|
||||
|
||||
LaunchedEffect(model.currentPage) {
|
||||
var sourceSelectDialog by remember { mutableStateOf(false) }
|
||||
|
||||
if (sourceSelectDialog)
|
||||
SourceSelectDialog(
|
||||
currentSource = name,
|
||||
onDismissRequest = { sourceSelectDialog = false }
|
||||
) {
|
||||
sourceSelectDialog = false
|
||||
navController.navigate("main/${it.name}") {
|
||||
launchSingleTop = true
|
||||
popUpTo("main/{source}") { inclusive = true }
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(model.currentPage, model.sortByPopularity) {
|
||||
model.search()
|
||||
}
|
||||
|
||||
@@ -99,11 +126,58 @@ class Hitomi(app: Application) : Source(), DIAware {
|
||||
)
|
||||
),
|
||||
actions = {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
|
||||
IconButton(onClick = { sourceSelectDialog = true }) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.hitomi),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = { expanded = true }) {
|
||||
Icon(Icons.Default.Sort, contentDescription = null)
|
||||
}
|
||||
|
||||
IconButton(onClick = { navController.navigate("settings") }) {
|
||||
Icon(Icons.Default.Settings, contentDescription = null)
|
||||
}
|
||||
|
||||
val onClick: (Boolean?) -> Unit = {
|
||||
expanded = false
|
||||
|
||||
it?.let {
|
||||
model.sortByPopularity = it
|
||||
}
|
||||
}
|
||||
DropdownMenu(expanded, onDismissRequest = { onClick(null) }) {
|
||||
DropdownMenuItem(onClick = { onClick(false) }) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.main_menu_sort_newest))
|
||||
RadioButton(selected = !model.sortByPopularity, onClick = { onClick(false) })
|
||||
}
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
DropdownMenuItem(onClick = { onClick(true) }){
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.main_menu_sort_popular))
|
||||
RadioButton(selected = model.sortByPopularity, onClick = { onClick(true) })
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onSearch = { model.search() }
|
||||
) {
|
||||
ListSearchResult(model.searchResults) {
|
||||
) { contentPadding ->
|
||||
ListSearchResult(model.searchResults, contentPadding = contentPadding) {
|
||||
DetailedSearchResult(
|
||||
it,
|
||||
bookmarks = bookmarkSet,
|
||||
@@ -114,14 +188,14 @@ class Hitomi(app: Application) : Source(), DIAware {
|
||||
}
|
||||
}
|
||||
) { result ->
|
||||
navController.navigate("reader/$name/${result.itemID}")
|
||||
navController.navigate("reader/${result.itemID}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Reader(navController: NavController) {
|
||||
fun Reader(navController: NavController) {
|
||||
val model: ReaderBaseViewModel = viewModel()
|
||||
|
||||
val database: AppDatabase by rememberInstance()
|
||||
@@ -148,7 +222,6 @@ class Hitomi(app: Application) : Source(), DIAware {
|
||||
append("Referer", getReferer(galleryID))
|
||||
}
|
||||
}.onFailure {
|
||||
logger.warning(it)
|
||||
model.error = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ import kotlinx.coroutines.yield
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.closestDI
|
||||
import org.kodein.di.instance
|
||||
import org.kodein.log.LoggerFactory
|
||||
import org.kodein.log.newLogger
|
||||
import xyz.quaver.pupil.db.AppDatabase
|
||||
import xyz.quaver.pupil.sources.composable.SearchBaseViewModel
|
||||
import xyz.quaver.pupil.sources.hitomi.lib.GalleryBlock
|
||||
@@ -41,6 +43,8 @@ import kotlin.math.ceil
|
||||
class HitomiSearchResultViewModel(app: Application) : SearchBaseViewModel<HitomiSearchResult>(app), DIAware {
|
||||
override val di by closestDI(app)
|
||||
|
||||
private val logger = newLogger(LoggerFactory.default)
|
||||
|
||||
private val client: HttpClient by instance()
|
||||
|
||||
private val database: AppDatabase by instance()
|
||||
@@ -80,9 +84,9 @@ class HitomiSearchResultViewModel(app: Application) : SearchBaseViewModel<Hitomi
|
||||
maxPage = ceil(result.size / resultsPerPage.toDouble()).toInt()
|
||||
}
|
||||
|
||||
yield()
|
||||
|
||||
cache.slice((currentPage-1)*resultsPerPage until currentPage*resultsPerPage).forEach { galleryID ->
|
||||
yield()
|
||||
loading = false
|
||||
searchResults.add(transform(getGalleryBlock(client, galleryID)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,25 +21,24 @@ package xyz.quaver.pupil.ui
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.google.accompanist.insets.ProvideWindowInsets
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.closestDI
|
||||
import org.kodein.di.direct
|
||||
import org.kodein.di.instance
|
||||
import org.kodein.log.LoggerFactory
|
||||
import org.kodein.log.newLogger
|
||||
import xyz.quaver.pupil.sources.SourceEntries
|
||||
import xyz.quaver.pupil.ui.theme.PupilTheme
|
||||
import xyz.quaver.pupil.ui.viewmodel.MainViewModel
|
||||
import xyz.quaver.pupil.util.source
|
||||
|
||||
|
||||
class MainActivity : ComponentActivity(), DIAware {
|
||||
override val di by closestDI()
|
||||
|
||||
private val model: MainViewModel by viewModels()
|
||||
private val sources: SourceEntries by instance()
|
||||
|
||||
private val logger = newLogger(LoggerFactory.default)
|
||||
|
||||
@@ -47,24 +46,17 @@ class MainActivity : ComponentActivity(), DIAware {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
setContent {
|
||||
PupilTheme {
|
||||
val navController = rememberNavController()
|
||||
ProvideWindowInsets {
|
||||
val navController = rememberNavController()
|
||||
|
||||
NavHost(navController, startDestination = "main/{source}") {
|
||||
composable("main/{source}") {
|
||||
direct.source(it.arguments?.getString("source") ?: "hitomi.la")
|
||||
.MainScreen(navController)
|
||||
}
|
||||
|
||||
composable("search/{source}") {
|
||||
direct.source(it.arguments?.getString("source") ?: "hitomi.la")
|
||||
.Search(navController)
|
||||
}
|
||||
|
||||
composable("reader/{source}/{itemID}") {
|
||||
direct.source(it.arguments?.getString("source") ?: "hitomi.la")
|
||||
.Reader(navController)
|
||||
NavHost(navController, startDestination = "hitomi.la") {
|
||||
sources.forEach {
|
||||
it.second.run { navGraph(navController) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Pupil, Hitomi.la viewer for Android
|
||||
* Copyright (C) 2021 tom5079
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.quaver.pupil.ui.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.x.closestDI
|
||||
import org.kodein.di.direct
|
||||
import org.kodein.log.LoggerFactory
|
||||
import org.kodein.log.newLogger
|
||||
import xyz.quaver.pupil.sources.Source
|
||||
import xyz.quaver.pupil.util.source
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
||||
override val di by closestDI()
|
||||
|
||||
private val logger = newLogger(LoggerFactory.default)
|
||||
|
||||
private val defaultSourceFactory: (String) -> Source = {
|
||||
direct.source(it)
|
||||
}
|
||||
private var sourceFactory: (String) -> Source = defaultSourceFactory
|
||||
var source by mutableStateOf(sourceFactory("hitomi.la"))
|
||||
}
|
||||
Reference in New Issue
Block a user