mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
feat: EmptyScreen to replace EmptyView
This commit is contained in:
parent
ff98cc65a9
commit
64c7b54075
7 changed files with 211 additions and 27 deletions
|
@ -11,6 +11,7 @@ import androidx.compose.material3.LargeTopAppBar
|
|||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarDefaults.topAppBarColors
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
|
@ -34,11 +35,12 @@ fun YokaiScaffold(
|
|||
onNavigationIconClicked: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
title: String = "",
|
||||
scrollBehavior: TopAppBarScrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(state = rememberTopAppBarState()),
|
||||
scrollBehavior: TopAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(state = rememberTopAppBarState()),
|
||||
fab: @Composable () -> Unit = {},
|
||||
navigationIcon: ImageVector = Icons.Filled.ArrowBack,
|
||||
navigationIconLabel: String = stringResource(id = R.string.back),
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
appBarType: AppBarType = AppBarType.LARGE,
|
||||
content: @Composable (PaddingValues) -> Unit,
|
||||
) {
|
||||
val view = LocalView.current
|
||||
|
@ -57,25 +59,46 @@ fun YokaiScaffold(
|
|||
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
floatingActionButton = fab,
|
||||
topBar = {
|
||||
LargeTopAppBar(
|
||||
title = {
|
||||
Text(text = title)
|
||||
},
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
colors = topAppBarColors(
|
||||
containerColor = color,
|
||||
scrolledContainerColor = color,
|
||||
),
|
||||
navigationIcon = {
|
||||
ToolTipButton(
|
||||
toolTipLabel = navigationIconLabel,
|
||||
icon = navigationIcon,
|
||||
buttonClicked = onNavigationIconClicked,
|
||||
)
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
actions = actions,
|
||||
)
|
||||
when (appBarType) {
|
||||
AppBarType.SMALL -> TopAppBar(
|
||||
title = {
|
||||
Text(text = title)
|
||||
},
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
colors = topAppBarColors(
|
||||
containerColor = color,
|
||||
scrolledContainerColor = color,
|
||||
),
|
||||
navigationIcon = {
|
||||
ToolTipButton(
|
||||
toolTipLabel = navigationIconLabel,
|
||||
icon = navigationIcon,
|
||||
buttonClicked = onNavigationIconClicked,
|
||||
)
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
actions = actions,
|
||||
)
|
||||
AppBarType.LARGE -> LargeTopAppBar(
|
||||
title = {
|
||||
Text(text = title)
|
||||
},
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
colors = topAppBarColors(
|
||||
containerColor = color,
|
||||
scrolledContainerColor = color,
|
||||
),
|
||||
navigationIcon = {
|
||||
ToolTipButton(
|
||||
toolTipLabel = navigationIconLabel,
|
||||
icon = navigationIcon,
|
||||
buttonClicked = onNavigationIconClicked,
|
||||
)
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
actions = actions,
|
||||
)
|
||||
}
|
||||
},
|
||||
content = content,
|
||||
)
|
||||
|
@ -88,3 +111,8 @@ fun getTopAppBarColor(title: String): Color {
|
|||
false -> MaterialTheme.colorScheme.surface.copy(alpha = .7f)
|
||||
}
|
||||
}
|
||||
|
||||
enum class AppBarType {
|
||||
SMALL,
|
||||
LARGE,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
package dev.yokai.presentation.component
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Download
|
||||
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.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.tachiyomi.util.compose.textHint
|
||||
|
||||
private val defaultIconModifier =
|
||||
Modifier.size(128.dp)
|
||||
|
||||
/**
|
||||
* Composable replacement for [eu.kanade.tachiyomi.widget.EmptyView]
|
||||
*/
|
||||
@Composable
|
||||
fun EmptyScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
image: ImageVector,
|
||||
message: String,
|
||||
actions: @Composable () -> Unit = {},
|
||||
) = EmptyScreen(
|
||||
modifier = modifier,
|
||||
image = {
|
||||
Image(
|
||||
modifier = defaultIconModifier,
|
||||
imageVector = image,
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.textHint),
|
||||
)
|
||||
},
|
||||
message = message,
|
||||
actions = actions,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun EmptyScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
image: ImageBitmap,
|
||||
message: String,
|
||||
actions: @Composable () -> Unit = {},
|
||||
) = EmptyScreen(
|
||||
modifier = modifier,
|
||||
image = {
|
||||
Image(
|
||||
modifier = defaultIconModifier,
|
||||
bitmap = image,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
message = message,
|
||||
actions = actions,
|
||||
)
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun EmptyScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
image: @Composable () -> Unit = {
|
||||
Image(modifier = defaultIconModifier, imageVector = Icons.Filled.Download, contentDescription = null)
|
||||
},
|
||||
message: String = "Something went wrong",
|
||||
actions: @Composable () -> Unit = {},
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
image()
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(top = 16.dp),
|
||||
text = message,
|
||||
color = MaterialTheme.colorScheme.textHint,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
)
|
||||
actions()
|
||||
}
|
||||
}
|
|
@ -3,14 +3,16 @@ package dev.yokai.presentation.extension.repo
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.ExtensionOff
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.yokai.presentation.AppBarType
|
||||
import dev.yokai.presentation.YokaiScaffold
|
||||
import dev.yokai.presentation.component.EmptyScreen
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
|
||||
@Composable
|
||||
|
@ -30,11 +32,13 @@ fun ExtensionRepoScreen(
|
|||
) {
|
||||
Icon(Icons.Filled.Add, "Add repo")
|
||||
}
|
||||
}
|
||||
},
|
||||
appBarType = AppBarType.SMALL,
|
||||
) { innerPadding ->
|
||||
Text(
|
||||
EmptyScreen(
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
text = "Hello World!"
|
||||
image = Icons.Filled.ExtensionOff,
|
||||
message = "No extension repo found",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
package dev.yokai.presentation.extension.repo
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dev.yokai.domain.Result
|
||||
import dev.yokai.domain.extension.repo.ExtensionRepoRepository
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.system.launchIO
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.update
|
||||
import okhttp3.internal.toImmutableList
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
@ -18,8 +21,11 @@ class ExtensionRepoViewModel :
|
|||
ViewModel() {
|
||||
|
||||
private val repository = ExtensionRepoRepository(Injekt.get())
|
||||
private val _repoState: MutableStateFlow<ExtensionRepoState> = MutableStateFlow(ExtensionRepoState.Loading)
|
||||
val repoState: StateFlow<ExtensionRepoState> = _repoState.asStateFlow()
|
||||
private val mutableRepoState: MutableStateFlow<ExtensionRepoState> = MutableStateFlow(ExtensionRepoState.Loading)
|
||||
val repoState: StateFlow<ExtensionRepoState> = mutableRepoState.asStateFlow()
|
||||
|
||||
private val internalEvent: MutableStateFlow<ExtensionRepoEvent> = MutableStateFlow(ExtensionRepoEvent.NoOp)
|
||||
val event: StateFlow<ExtensionRepoEvent> = internalEvent.asStateFlow()
|
||||
|
||||
init {
|
||||
refresh()
|
||||
|
@ -29,7 +35,13 @@ class ExtensionRepoViewModel :
|
|||
viewModelScope.launchIO {
|
||||
val result = repository.addRepo(url)
|
||||
if (result is Result.Error) return@launchIO
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteRepo(repo: String) {
|
||||
viewModelScope.launchIO {
|
||||
repository.deleteRepo(repo)
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
@ -37,12 +49,18 @@ class ExtensionRepoViewModel :
|
|||
fun refresh() {
|
||||
viewModelScope.launchIO {
|
||||
repository.getRepo().collectLatest { repos ->
|
||||
_repoState.value = ExtensionRepoState.Success(repos = repos.toImmutableList())
|
||||
mutableRepoState.update { ExtensionRepoState.Success(repos = repos.toImmutableList()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ExtensionRepoEvent {
|
||||
sealed class LocalizedMessage(@StringRes val stringRes: Int) : ExtensionRepoEvent()
|
||||
data object InvalidUrl : LocalizedMessage(R.string.invalid_repo_url)
|
||||
data object NoOp : ExtensionRepoEvent()
|
||||
}
|
||||
|
||||
sealed class ExtensionRepoState {
|
||||
|
||||
@Immutable
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package dev.yokai.presentation.extension.repo.component
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
@Composable
|
||||
fun ExtensionRepoItem(
|
||||
modifier: Modifier = Modifier,
|
||||
repoUrl: String,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = repoUrl,
|
||||
)
|
||||
Image(imageVector = Icons.Filled.Delete, contentDescription = null)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package eu.kanade.tachiyomi.util.compose
|
||||
|
||||
import androidx.compose.material3.ColorScheme
|
||||
|
||||
val ColorScheme.textHint get() = onBackground.copy(alpha = 0.35f)
|
|
@ -905,7 +905,11 @@
|
|||
<string name="nsfw_sources">NSFW (18+) sources</string>
|
||||
<string name="show_in_sources_and_extensions">Show in sources and extensions lists</string>
|
||||
<string name="does_not_prevent_unofficial_nsfw">This does not prevent unofficial or potentially incorrectly flagged extensions from surfacing NSFW (18+) content within the app.</string>
|
||||
|
||||
<!-- Extension repos -->
|
||||
<string name="source_repos">Extension Repos</string>
|
||||
<string name="action_add_repo">Add repo</string>
|
||||
<string name="invalid_repo_url">Invalid repo url</string>
|
||||
|
||||
<!-- About section -->
|
||||
<string name="version">Version</string>
|
||||
|
@ -1189,4 +1193,5 @@
|
|||
<string name="warning">Warning</string>
|
||||
<string name="wifi">Wi-Fi</string>
|
||||
<string name="webcomic">Webcomic</string>
|
||||
<string name="internal_error">Internal error: %s</string>
|
||||
</resources>
|
Loading…
Add table
Add a link
Reference in a new issue