package eu.kanade.presentation.browse import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.items 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.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import eu.kanade.presentation.browse.components.BaseSourceItem import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.browse.source.SourcesScreenModel import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing import eu.kanade.tachiyomi.util.system.LocaleHelper import tachiyomi.domain.source.model.Pin import tachiyomi.domain.source.model.Source import tachiyomi.presentation.core.components.ScrollbarLazyColumn import tachiyomi.presentation.core.components.material.SecondaryItemAlpha import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.topSmallPaddingValues import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.theme.header import tachiyomi.presentation.core.util.plus import tachiyomi.source.local.isLocal @Composable fun SourcesScreen( state: SourcesScreenModel.State, contentPadding: PaddingValues, onClickItem: (Source, Listing) -> Unit, onClickPin: (Source) -> Unit, onLongClickItem: (Source) -> Unit, ) { when { state.isLoading -> LoadingScreen(modifier = Modifier.padding(contentPadding)) state.isEmpty -> EmptyScreen( textResource = R.string.source_empty_screen, modifier = Modifier.padding(contentPadding), ) else -> { ScrollbarLazyColumn( contentPadding = contentPadding + topSmallPaddingValues, ) { items( items = state.items, contentType = { when (it) { is SourceUiModel.Header -> "header" is SourceUiModel.Item -> "item" } }, key = { when (it) { is SourceUiModel.Header -> it.hashCode() is SourceUiModel.Item -> "source-${it.source.key()}" } }, ) { model -> when (model) { is SourceUiModel.Header -> { SourceHeader( modifier = Modifier.animateItemPlacement(), language = model.language, ) } is SourceUiModel.Item -> SourceItem( modifier = Modifier.animateItemPlacement(), source = model.source, onClickItem = onClickItem, onLongClickItem = onLongClickItem, onClickPin = onClickPin, ) } } } } } } @Composable private fun SourceHeader( modifier: Modifier = Modifier, language: String, ) { val context = LocalContext.current Text( text = LocaleHelper.getSourceDisplayName(language, context), modifier = modifier .padding(horizontal = MaterialTheme.padding.medium, vertical = MaterialTheme.padding.small), style = MaterialTheme.typography.header, ) } @Composable private fun SourceItem( modifier: Modifier = Modifier, source: Source, onClickItem: (Source, Listing) -> Unit, onLongClickItem: (Source) -> Unit, onClickPin: (Source) -> Unit, ) { BaseSourceItem( modifier = modifier, source = source, onClickItem = { onClickItem(source, Listing.Popular) }, onLongClickItem = { onLongClickItem(source) }, action = { if (source.supportsLatest) { TextButton(onClick = { onClickItem(source, Listing.Latest) }) { Text( text = stringResource(R.string.latest), style = LocalTextStyle.current.copy( color = MaterialTheme.colorScheme.primary, ), ) } } SourcePinButton( isPinned = Pin.Pinned in source.pin, onClick = { onClickPin(source) }, ) }, ) } @Composable private fun SourcePinButton( isPinned: Boolean, onClick: () -> Unit, ) { val icon = if (isPinned) Icons.Filled.PushPin else Icons.Outlined.PushPin val tint = if (isPinned) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onBackground.copy(alpha = SecondaryItemAlpha) val description = if (isPinned) R.string.action_unpin else R.string.action_pin IconButton(onClick = onClick) { Icon( imageVector = icon, tint = tint, contentDescription = stringResource(description), ) } } @Composable fun SourceOptionsDialog( source: Source, onClickPin: () -> Unit, onClickDisable: () -> Unit, onDismiss: () -> Unit, ) { AlertDialog( title = { Text(text = source.visualName) }, text = { Column { val textId = if (Pin.Pinned in source.pin) R.string.action_unpin else R.string.action_pin Text( text = stringResource(textId), modifier = Modifier .clickable(onClick = onClickPin) .fillMaxWidth() .padding(vertical = 16.dp), ) if (!source.isLocal()) { Text( text = stringResource(R.string.action_disable), modifier = Modifier .clickable(onClick = onClickDisable) .fillMaxWidth() .padding(vertical = 16.dp), ) } } }, onDismissRequest = onDismiss, confirmButton = {}, ) } sealed interface SourceUiModel { data class Item(val source: Source) : SourceUiModel data class Header(val language: String) : SourceUiModel }