Use Compose in Migrate tab (#7008)

* Use Compose in Migrate tab

* Add missing header

* Remove unused files

* Fix build after rebase

* Changes from review comments
This commit is contained in:
Andreas
2022-04-27 14:36:16 +02:00
committed by GitHub
parent a4a4503311
commit 7261fcccda
20 changed files with 432 additions and 456 deletions

View File

@@ -0,0 +1,16 @@
package eu.kanade.presentation.components
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun LoadingScreen() {
Box(modifier = Modifier.fillMaxSize()) {
CircularProgressIndicator(modifier = Modifier.size(64.dp))
}
}

View File

@@ -0,0 +1,117 @@
package eu.kanade.presentation.source
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import eu.kanade.domain.source.model.Source
import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.LoadingScreen
import eu.kanade.presentation.source.components.BaseSourceItem
import eu.kanade.presentation.theme.header
import eu.kanade.presentation.util.horizontalPadding
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrationSourcesPresenter
@Composable
fun MigrateSourceScreen(
nestedScrollInterop: NestedScrollConnection,
presenter: MigrationSourcesPresenter,
onClickItem: (Source) -> Unit,
onLongClickItem: (Source) -> Unit,
) {
val state by presenter.state.collectAsState()
when {
state.isLoading -> LoadingScreen()
state.isEmpty -> EmptyScreen(textResource = R.string.information_empty_library)
else -> {
MigrateSourceList(
nestedScrollInterop = nestedScrollInterop,
list = state.sources!!,
onClickItem = onClickItem,
onLongClickItem = onLongClickItem,
)
}
}
}
@Composable
fun MigrateSourceList(
nestedScrollInterop: NestedScrollConnection,
list: List<Pair<Source, Long>>,
onClickItem: (Source) -> Unit,
onLongClickItem: (Source) -> Unit,
) {
LazyColumn(
modifier = Modifier.nestedScroll(nestedScrollInterop),
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
) {
item(key = "title") {
Text(
text = stringResource(id = R.string.migration_selection_prompt),
modifier = Modifier
.animateItemPlacement()
.padding(horizontal = horizontalPadding, vertical = 8.dp),
style = MaterialTheme.typography.header
)
}
items(
items = list,
key = { (source, _) ->
source.id
}
) { (source, count) ->
MigrateSourceItem(
modifier = Modifier.animateItemPlacement(),
source = source,
count = count,
onClickItem = { onClickItem(source) },
onLongClickItem = { onLongClickItem(source) }
)
}
}
}
@Composable
fun MigrateSourceItem(
modifier: Modifier = Modifier,
source: Source,
count: Long,
onClickItem: () -> Unit,
onLongClickItem: () -> Unit,
) {
BaseSourceItem(
modifier = modifier,
source = source,
onClickItem = onClickItem,
onLongClickItem = onLongClickItem,
action = {
Text(
text = "$count",
modifier = Modifier
.clip(RoundedCornerShape(4.dp))
.background(MaterialTheme.colorScheme.primary)
.padding(horizontal = 8.dp, vertical = 2.dp),
style = MaterialTheme.typography.bodyMedium.copy(
color = MaterialTheme.colorScheme.onPrimary
)
)
}
)
}

View File

@@ -2,9 +2,7 @@ package eu.kanade.presentation.source
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.aspectRatio
@@ -18,7 +16,6 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PushPin
import androidx.compose.material.icons.outlined.PushPin
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalTextStyle
@@ -30,18 +27,18 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import eu.kanade.domain.source.model.Pin
import eu.kanade.domain.source.model.Source
import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.LoadingScreen
import eu.kanade.presentation.source.components.BaseSourceItem
import eu.kanade.presentation.theme.header
import eu.kanade.presentation.util.horizontalPadding
import eu.kanade.tachiyomi.R
@@ -62,7 +59,7 @@ fun SourceScreen(
val state by presenter.state.collectAsState()
when {
state.isLoading -> CircularProgressIndicator()
state.isLoading -> LoadingScreen()
state.hasError -> Text(text = state.error!!.message!!)
state.isEmpty -> EmptyScreen(message = "")
else -> SourceList(
@@ -115,7 +112,7 @@ fun SourceList(
}
is UiModel.Item -> SourceItem(
modifier = Modifier.animateItemPlacement(),
item = model.source,
source = model.source,
onClickItem = onClickItem,
onLongClickItem = {
setSourceState(it)
@@ -160,55 +157,34 @@ fun SourceHeader(
@Composable
fun SourceItem(
modifier: Modifier = Modifier,
item: Source,
source: Source,
onClickItem: (Source) -> Unit,
onLongClickItem: (Source) -> Unit,
onClickLatest: (Source) -> Unit,
onClickPin: (Source) -> Unit
) {
Row(
modifier = modifier
.combinedClickable(
onClick = { onClickItem(item) },
onLongClick = { onLongClickItem(item) }
)
.padding(horizontal = horizontalPadding, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
SourceIcon(source = item)
Column(
modifier = Modifier
.padding(horizontal = horizontalPadding)
.weight(1f)
) {
Text(
text = item.name,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.bodyMedium
)
Text(
text = LocaleHelper.getDisplayName(item.lang),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.bodySmall
)
}
if (item.supportsLatest) {
TextButton(onClick = { onClickLatest(item) }) {
Text(
text = stringResource(id = R.string.latest),
style = LocalTextStyle.current.copy(
color = MaterialTheme.colorScheme.primary
),
)
BaseSourceItem(
modifier = modifier,
source = source,
onClickItem = { onClickItem(source) },
onLongClickItem = { onLongClickItem(source) },
action = { source ->
if (source.supportsLatest) {
TextButton(onClick = { onClickLatest(source) }) {
Text(
text = stringResource(id = R.string.latest),
style = LocalTextStyle.current.copy(
color = MaterialTheme.colorScheme.primary
)
)
}
}
}
SourcePinButton(
isPinned = Pin.Pinned in item.pin,
onClick = { onClickPin(item) }
)
}
SourcePinButton(
isPinned = Pin.Pinned in source.pin,
onClick = { onClickPin(source) }
)
},
)
}
@Composable

View File

@@ -0,0 +1,68 @@
package eu.kanade.presentation.source.components
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import eu.kanade.domain.source.model.Source
import eu.kanade.presentation.source.SourceIcon
import eu.kanade.presentation.util.horizontalPadding
import eu.kanade.tachiyomi.util.system.LocaleHelper
@Composable
fun BaseSourceItem(
modifier: Modifier = Modifier,
source: Source,
onClickItem: () -> Unit = {},
onLongClickItem: () -> Unit = {},
icon: @Composable RowScope.(Source) -> Unit = defaultIcon,
action: @Composable RowScope.(Source) -> Unit = {},
content: @Composable RowScope.(Source) -> Unit = defaultContent,
) {
Row(
modifier = modifier
.combinedClickable(
onClick = onClickItem,
onLongClick = onLongClickItem
)
.padding(horizontal = horizontalPadding, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
icon.invoke(this, source)
content.invoke(this, source)
action.invoke(this, source)
}
}
private val defaultIcon: @Composable RowScope.(Source) -> Unit = { source ->
SourceIcon(source = source)
}
private val defaultContent: @Composable RowScope.(Source) -> Unit = { source ->
Column(
modifier = Modifier
.padding(horizontal = horizontalPadding)
.weight(1f)
) {
Text(
text = source.name,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.bodyMedium
)
Text(
text = LocaleHelper.getDisplayName(source.lang),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.bodySmall
)
}
}