Nested navigation
Hitomi main actions
This commit is contained in:
tom5079
2021-12-19 12:33:47 +09:00
parent 7befa24aff
commit 20ddf04614
11 changed files with 192 additions and 131 deletions

View File

@@ -12,6 +12,6 @@
</deviceKey> </deviceKey>
</Target> </Target>
</runningDeviceTargetSelectedWithDropDown> </runningDeviceTargetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2021-12-18T14:48:54.587703Z" /> <timeTargetWasSelectedWithDropDown value="2021-12-19T03:31:58.153375Z" />
</component> </component>
</project> </project>

View File

@@ -19,8 +19,8 @@
package xyz.quaver.pupil.sources package xyz.quaver.pupil.sources
import android.app.Application import android.app.Application
import androidx.compose.runtime.Composable
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import org.kodein.di.* import org.kodein.di.*
import xyz.quaver.pupil.sources.hitomi.Hitomi import xyz.quaver.pupil.sources.hitomi.Hitomi
@@ -28,14 +28,7 @@ abstract class Source {
abstract val name: String abstract val name: String
abstract val iconResID: Int abstract val iconResID: Int
@Composable open fun NavGraphBuilder.navGraph(navController: NavController) { }
open fun MainScreen(navController: NavController) { }
@Composable
open fun Search(navController: NavController) { }
@Composable
open fun Reader(navController: NavController) { }
} }
typealias SourceEntry = Pair<String, Source> typealias SourceEntry = Pair<String, Source>

View File

@@ -24,10 +24,7 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Card import androidx.compose.material.*
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Close
import androidx.compose.runtime.* import androidx.compose.runtime.*
@@ -80,7 +77,8 @@ fun FloatingSearchBar(
elevation = 8.dp elevation = 8.dp
) { ) {
Row( Row(
modifier = Modifier.fillMaxSize().padding(16.dp, 0.dp), modifier = Modifier
.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
navigationIcon() navigationIcon()
@@ -91,14 +89,15 @@ fun FloatingSearchBar(
.padding(16.dp, 0.dp) .padding(16.dp, 0.dp)
.onFocusChanged { .onFocusChanged {
if (it.isFocused) onTextFieldFocused() if (it.isFocused) onTextFieldFocused()
else onTextFieldUnfocused() else onTextFieldUnfocused()
isFocused = it.isFocused isFocused = it.isFocused
}, },
value = query, value = query,
onValueChange = onQueryChange, onValueChange = onQueryChange,
singleLine = true, 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), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
keyboardActions = KeyboardActions( keyboardActions = KeyboardActions(
onSearch = { onSearch = {
@@ -129,11 +128,14 @@ fun FloatingSearchBar(
} }
) )
Row( CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
verticalAlignment = Alignment.CenterVertically, Row(
horizontalArrangement = Arrangement.spacedBy(24.dp), Modifier.fillMaxHeight(),
content = actions horizontalArrangement = Arrangement.End,
) verticalAlignment = Alignment.CenterVertically,
content = actions
)
}
} }
} }
} }

View File

@@ -28,10 +28,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@Composable @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( LazyColumn(
Modifier.fillMaxSize(), Modifier.fillMaxSize(),
contentPadding = PaddingValues(0.dp, 64.dp, 0.dp, 0.dp), contentPadding = contentPadding,
verticalArrangement = Arrangement.spacedBy(8.dp) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
items(searchResults) { itemInfo -> items(searchResults) { itemInfo ->

View File

@@ -126,6 +126,7 @@ private class FloatingActionButtonItemProvider : PreviewParameterProvider<SubFab
@Composable @Composable
fun MultipleFloatingActionButton( fun MultipleFloatingActionButton(
@PreviewParameter(provider = FloatingActionButtonItemProvider::class) items: List<SubFabItem>, @PreviewParameter(provider = FloatingActionButtonItemProvider::class) items: List<SubFabItem>,
modifier: Modifier = Modifier,
fabIcon: ImageVector = Icons.Default.Add, fabIcon: ImageVector = Icons.Default.Add,
visible: Boolean = true, visible: Boolean = true,
targetState: FloatingActionButtonState = FloatingActionButtonState.COLLAPSED, targetState: FloatingActionButtonState = FloatingActionButtonState.COLLAPSED,
@@ -150,6 +151,7 @@ fun MultipleFloatingActionButton(
if (!visible) onStateChanged?.invoke(FloatingActionButtonState.COLLAPSED) if (!visible) onStateChanged?.invoke(FloatingActionButtonState.COLLAPSED)
Column( Column(
modifier = modifier,
horizontalAlignment = Alignment.End, horizontalAlignment = Alignment.End,
verticalArrangement = Arrangement.spacedBy(12.dp) verticalArrangement = Arrangement.spacedBy(12.dp)
) { ) {

View File

@@ -41,6 +41,11 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope 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.client.request.*
import io.ktor.http.* import io.ktor.http.*
import kotlinx.coroutines.ExperimentalCoroutinesApi 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( Scaffold(
topBar = { topBar = {
if (!model.isFullscreen) if (!model.isFullscreen)
@@ -186,7 +201,11 @@ fun ReaderBase(
}, },
actions = { actions = {
//TODO //TODO
} },
contentPadding = rememberInsetsPaddingValues(
LocalWindowInsets.current.statusBars,
applyBottom = false
)
) )
}, },
floatingActionButton = { floatingActionButton = {
@@ -208,8 +227,8 @@ fun ReaderBase(
}, },
scaffoldState = scaffoldState, scaffoldState = scaffoldState,
snackbarHost = { scaffoldState.snackbarHostState } snackbarHost = { scaffoldState.snackbarHostState }
) { ) { contentPadding ->
Box { Box(Modifier.padding(contentPadding)) {
LazyColumn( LazyColumn(
Modifier.fillMaxSize(), Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(4.dp) verticalArrangement = Arrangement.spacedBy(4.dp)

View File

@@ -24,20 +24,18 @@ import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.updateTransition import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.Canvas import androidx.compose.foundation.Canvas
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.forEachGesture import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
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.material.icons.filled.NavigateNext import androidx.compose.material.icons.filled.NavigateNext
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.* import androidx.compose.runtime.*
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.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.NestedScrollSource
@@ -55,6 +53,12 @@ import androidx.compose.ui.util.fastFirstOrNull
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.drawablepainter.rememberDrawablePainter 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.R
import xyz.quaver.pupil.ui.theme.LightBlue300 import xyz.quaver.pupil.ui.theme.LightBlue300
import kotlin.math.* import kotlin.math.*
@@ -95,7 +99,7 @@ fun <T> SearchBase(
fabSubMenu: List<SubFabItem> = emptyList(), fabSubMenu: List<SubFabItem> = emptyList(),
actions: @Composable RowScope.() -> Unit = { }, actions: @Composable RowScope.() -> Unit = { },
onSearch: () -> Unit = { }, onSearch: () -> Unit = { },
content: @Composable BoxScope.() -> Unit content: @Composable BoxScope.(contentPadding: PaddingValues) -> Unit
) { ) {
val context = LocalContext.current val context = LocalContext.current
val focusManager = LocalFocusManager.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 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) } 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) { LaunchedEffect(navigationIconProgress) {
navigationIcon.progress = navigationIconProgress navigationIcon.progress = navigationIconProgress
} }
@@ -127,6 +145,7 @@ fun <T> SearchBase(
Scaffold( Scaffold(
floatingActionButton = { floatingActionButton = {
MultipleFloatingActionButton( MultipleFloatingActionButton(
modifier = Modifier.navigationBarsPadding(),
items = fabSubMenu, items = fabSubMenu,
visible = model.isFabVisible, visible = model.isFabVisible,
targetState = isFabExpanded, targetState = isFabExpanded,
@@ -135,8 +154,8 @@ fun <T> SearchBase(
} }
) )
} }
) { ) { contentPadding ->
Box(Modifier.fillMaxSize()) { Box(Modifier.padding(contentPadding)) {
val topCircleRadius by animateFloatAsState(if (overscroll?.let { it >= pageTurnIndicatorHeight } == true) 1000f else 0f) 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) val bottomCircleRadius by animateFloatAsState(if (overscroll?.let { it <= -pageTurnIndicatorHeight } == true) 1000f else 0f)
@@ -144,7 +163,7 @@ fun <T> SearchBase(
Canvas(modifier = Modifier.fillMaxSize()) { Canvas(modifier = Modifier.fillMaxSize()) {
drawCircle( drawCircle(
LightBlue300.copy(alpha = 0.6f), LightBlue300.copy(alpha = 0.6f),
center = Offset(this.center.x, searchBarHeight.toFloat()), center = Offset(this.center.x, searchBarDefaultOffsetPx.toFloat()),
radius = topCircleRadius radius = topCircleRadius
) )
drawCircle( drawCircle(
@@ -195,7 +214,9 @@ fun <T> SearchBase(
modifier = Modifier modifier = Modifier
.offset( .offset(
0.dp, 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) ?: 0.dp)
.nestedScroll(object : NestedScrollConnection { .nestedScroll(object : NestedScrollConnection {
override fun onPreScroll( override fun onPreScroll(
@@ -207,7 +228,7 @@ fun <T> SearchBase(
if (overscrollSnapshot == null || overscrollSnapshot == 0f) { if (overscrollSnapshot == null || overscrollSnapshot == 0f) {
model.searchBarOffset = model.searchBarOffset =
(model.searchBarOffset + available.y.roundToInt()).coerceIn( (model.searchBarOffset + available.y.roundToInt()).coerceIn(
-searchBarHeight, -searchBarDefaultOffsetPx,
0 0
) )
@@ -275,29 +296,29 @@ fun <T> SearchBase(
} }
} }
}, },
content = content content = {
this.content(
PaddingValues(0.dp, searchBarDefaultOffset, 0.dp, 0.dp)
)
}
) )
if (model.loading) if (model.loading)
CircularProgressIndicator(Modifier.align(Alignment.Center)) CircularProgressIndicator(Modifier.align(Alignment.Center))
FloatingSearchBar( 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, query = model.query,
onQueryChange = { model.query = it }, onQueryChange = { model.query = it },
navigationIcon = { navigationIcon = {
Icon( IconButton(onClick = { focusManager.clearFocus() }) {
painter = rememberDrawablePainter(navigationIcon), Icon(
contentDescription = null, painter = rememberDrawablePainter(navigationIcon),
modifier = Modifier contentDescription = null
.size(24.dp) )
.clickable( }
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(bounded = false)
) {
focusManager.clearFocus()
}
)
}, },
actions = actions, actions = actions,
onTextFieldFocused = { navigationIconState = NavigationIconState.ARROW }, onTextFieldFocused = { navigationIconState = NavigationIconState.ARROW },

View File

@@ -19,14 +19,27 @@
package xyz.quaver.pupil.sources.hitomi package xyz.quaver.pupil.sources.hitomi
import android.app.Application 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.Icons
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.Shuffle import androidx.compose.material.icons.filled.Shuffle
import androidx.compose.material.icons.filled.Sort
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState 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.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import androidx.navigation.compose.navigation
import io.ktor.client.* import io.ktor.client.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch 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.getGalleryInfo
import xyz.quaver.pupil.sources.hitomi.lib.getReferer import xyz.quaver.pupil.sources.hitomi.lib.getReferer
import xyz.quaver.pupil.sources.hitomi.lib.imageUrlFromImage import xyz.quaver.pupil.sources.hitomi.lib.imageUrlFromImage
import xyz.quaver.pupil.ui.dialog.SourceSelectDialog
class Hitomi(app: Application) : Source(), DIAware { class Hitomi(app: Application) : Source(), DIAware {
override val di by closestDI(app) override val di by closestDI(app)
@@ -58,16 +72,15 @@ class Hitomi(app: Application) : Source(), DIAware {
override val name: String = "hitomi.la" override val name: String = "hitomi.la"
override val iconResID: Int = R.drawable.hitomi override val iconResID: Int = R.drawable.hitomi
@Composable override fun NavGraphBuilder.navGraph(navController: NavController) {
override fun MainScreen(navController: NavController) { navigation(startDestination = "search", route = name) {
navController.navigate("search/hitomi.la") { composable("search") { Search(navController) }
launchSingleTop = true composable("reader/{itemID}") { Reader(navController) }
navController.popBackStack()
} }
} }
@Composable @Composable
override fun Search(navController: NavController) { fun Search(navController: NavController) {
val model: HitomiSearchResultViewModel = viewModel() val model: HitomiSearchResultViewModel = viewModel()
val database: AppDatabase by rememberInstance() val database: AppDatabase by rememberInstance()
val bookmarkDao = remember { database.bookmarkDao() } val bookmarkDao = remember { database.bookmarkDao() }
@@ -78,7 +91,21 @@ class Hitomi(app: Application) : Source(), DIAware {
bookmarks?.toSet() ?: emptySet() 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() model.search()
} }
@@ -99,11 +126,58 @@ class Hitomi(app: Application) : Source(), DIAware {
) )
), ),
actions = { 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() } onSearch = { model.search() }
) { ) { contentPadding ->
ListSearchResult(model.searchResults) { ListSearchResult(model.searchResults, contentPadding = contentPadding) {
DetailedSearchResult( DetailedSearchResult(
it, it,
bookmarks = bookmarkSet, bookmarks = bookmarkSet,
@@ -114,14 +188,14 @@ class Hitomi(app: Application) : Source(), DIAware {
} }
} }
) { result -> ) { result ->
navController.navigate("reader/$name/${result.itemID}") navController.navigate("reader/${result.itemID}")
} }
} }
} }
} }
@Composable @Composable
override fun Reader(navController: NavController) { fun Reader(navController: NavController) {
val model: ReaderBaseViewModel = viewModel() val model: ReaderBaseViewModel = viewModel()
val database: AppDatabase by rememberInstance() val database: AppDatabase by rememberInstance()
@@ -148,7 +222,6 @@ class Hitomi(app: Application) : Source(), DIAware {
append("Referer", getReferer(galleryID)) append("Referer", getReferer(galleryID))
} }
}.onFailure { }.onFailure {
logger.warning(it)
model.error = true model.error = true
} }
} }

View File

@@ -31,6 +31,8 @@ import kotlinx.coroutines.yield
import org.kodein.di.DIAware import org.kodein.di.DIAware
import org.kodein.di.android.closestDI 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.newLogger
import xyz.quaver.pupil.db.AppDatabase import xyz.quaver.pupil.db.AppDatabase
import xyz.quaver.pupil.sources.composable.SearchBaseViewModel import xyz.quaver.pupil.sources.composable.SearchBaseViewModel
import xyz.quaver.pupil.sources.hitomi.lib.GalleryBlock import xyz.quaver.pupil.sources.hitomi.lib.GalleryBlock
@@ -41,6 +43,8 @@ import kotlin.math.ceil
class HitomiSearchResultViewModel(app: Application) : SearchBaseViewModel<HitomiSearchResult>(app), DIAware { class HitomiSearchResultViewModel(app: Application) : SearchBaseViewModel<HitomiSearchResult>(app), DIAware {
override val di by closestDI(app) override val di by closestDI(app)
private val logger = newLogger(LoggerFactory.default)
private val client: HttpClient by instance() private val client: HttpClient by instance()
private val database: AppDatabase 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() maxPage = ceil(result.size / resultsPerPage.toDouble()).toInt()
} }
yield()
cache.slice((currentPage-1)*resultsPerPage until currentPage*resultsPerPage).forEach { galleryID -> cache.slice((currentPage-1)*resultsPerPage until currentPage*resultsPerPage).forEach { galleryID ->
yield()
loading = false
searchResults.add(transform(getGalleryBlock(client, galleryID))) searchResults.add(transform(getGalleryBlock(client, galleryID)))
} }
} }

View File

@@ -21,25 +21,24 @@ package xyz.quaver.pupil.ui
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.core.view.WindowCompat
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.google.accompanist.insets.ProvideWindowInsets
import org.kodein.di.DIAware import org.kodein.di.DIAware
import org.kodein.di.android.closestDI 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.LoggerFactory
import org.kodein.log.newLogger import org.kodein.log.newLogger
import xyz.quaver.pupil.sources.SourceEntries
import xyz.quaver.pupil.ui.theme.PupilTheme import xyz.quaver.pupil.ui.theme.PupilTheme
import xyz.quaver.pupil.ui.viewmodel.MainViewModel
import xyz.quaver.pupil.util.source
class MainActivity : ComponentActivity(), DIAware { class MainActivity : ComponentActivity(), DIAware {
override val di by closestDI() override val di by closestDI()
private val model: MainViewModel by viewModels() private val sources: SourceEntries by instance()
private val logger = newLogger(LoggerFactory.default) private val logger = newLogger(LoggerFactory.default)
@@ -47,24 +46,17 @@ class MainActivity : ComponentActivity(), DIAware {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent { setContent {
PupilTheme { PupilTheme {
val navController = rememberNavController() ProvideWindowInsets {
val navController = rememberNavController()
NavHost(navController, startDestination = "main/{source}") { NavHost(navController, startDestination = "hitomi.la") {
composable("main/{source}") { sources.forEach {
direct.source(it.arguments?.getString("source") ?: "hitomi.la") it.second.run { navGraph(navController) }
.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)
} }
} }
} }

View File

@@ -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"))
}