BrowseSourceScreen.kt 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. package eu.kanade.presentation.browse
  2. import androidx.compose.foundation.layout.PaddingValues
  3. import androidx.compose.foundation.layout.padding
  4. import androidx.compose.foundation.lazy.grid.GridCells
  5. import androidx.compose.material.icons.Icons
  6. import androidx.compose.material.icons.outlined.HelpOutline
  7. import androidx.compose.material.icons.outlined.Public
  8. import androidx.compose.material.icons.outlined.Refresh
  9. import androidx.compose.material3.SnackbarDuration
  10. import androidx.compose.material3.SnackbarHostState
  11. import androidx.compose.material3.SnackbarResult
  12. import androidx.compose.runtime.Composable
  13. import androidx.compose.runtime.LaunchedEffect
  14. import androidx.compose.ui.Modifier
  15. import androidx.compose.ui.platform.LocalContext
  16. import androidx.compose.ui.res.stringResource
  17. import androidx.paging.LoadState
  18. import androidx.paging.compose.LazyPagingItems
  19. import eu.kanade.presentation.browse.components.BrowseSourceComfortableGrid
  20. import eu.kanade.presentation.browse.components.BrowseSourceCompactGrid
  21. import eu.kanade.presentation.browse.components.BrowseSourceList
  22. import eu.kanade.presentation.components.AppBar
  23. import eu.kanade.presentation.util.formattedMessage
  24. import eu.kanade.tachiyomi.R
  25. import eu.kanade.tachiyomi.source.Source
  26. import kotlinx.coroutines.flow.StateFlow
  27. import tachiyomi.domain.library.model.LibraryDisplayMode
  28. import tachiyomi.domain.manga.model.Manga
  29. import tachiyomi.domain.source.model.StubSource
  30. import tachiyomi.presentation.core.components.material.Scaffold
  31. import tachiyomi.presentation.core.screens.EmptyScreen
  32. import tachiyomi.presentation.core.screens.EmptyScreenAction
  33. import tachiyomi.presentation.core.screens.LoadingScreen
  34. import tachiyomi.source.local.LocalSource
  35. @Composable
  36. fun BrowseSourceContent(
  37. source: Source?,
  38. mangaList: LazyPagingItems<StateFlow<Manga>>,
  39. columns: GridCells,
  40. displayMode: LibraryDisplayMode,
  41. snackbarHostState: SnackbarHostState,
  42. contentPadding: PaddingValues,
  43. onWebViewClick: () -> Unit,
  44. onHelpClick: () -> Unit,
  45. onLocalSourceHelpClick: () -> Unit,
  46. onMangaClick: (Manga) -> Unit,
  47. onMangaLongClick: (Manga) -> Unit,
  48. ) {
  49. val context = LocalContext.current
  50. val errorState = mangaList.loadState.refresh.takeIf { it is LoadState.Error }
  51. ?: mangaList.loadState.append.takeIf { it is LoadState.Error }
  52. val getErrorMessage: (LoadState.Error) -> String = { state ->
  53. with(context) { state.error.formattedMessage }
  54. }
  55. LaunchedEffect(errorState) {
  56. if (mangaList.itemCount > 0 && errorState != null && errorState is LoadState.Error) {
  57. val result = snackbarHostState.showSnackbar(
  58. message = getErrorMessage(errorState),
  59. actionLabel = context.getString(R.string.action_retry),
  60. duration = SnackbarDuration.Indefinite,
  61. )
  62. when (result) {
  63. SnackbarResult.Dismissed -> snackbarHostState.currentSnackbarData?.dismiss()
  64. SnackbarResult.ActionPerformed -> mangaList.retry()
  65. }
  66. }
  67. }
  68. if (mangaList.itemCount <= 0 && errorState != null && errorState is LoadState.Error) {
  69. EmptyScreen(
  70. modifier = Modifier.padding(contentPadding),
  71. message = getErrorMessage(errorState),
  72. actions = if (source is LocalSource) {
  73. listOf(
  74. EmptyScreenAction(
  75. stringResId = R.string.local_source_help_guide,
  76. icon = Icons.Outlined.HelpOutline,
  77. onClick = onLocalSourceHelpClick,
  78. ),
  79. )
  80. } else {
  81. listOf(
  82. EmptyScreenAction(
  83. stringResId = R.string.action_retry,
  84. icon = Icons.Outlined.Refresh,
  85. onClick = mangaList::refresh,
  86. ),
  87. EmptyScreenAction(
  88. stringResId = R.string.action_open_in_web_view,
  89. icon = Icons.Outlined.Public,
  90. onClick = onWebViewClick,
  91. ),
  92. EmptyScreenAction(
  93. stringResId = R.string.label_help,
  94. icon = Icons.Outlined.HelpOutline,
  95. onClick = onHelpClick,
  96. ),
  97. )
  98. },
  99. )
  100. return
  101. }
  102. if (mangaList.itemCount == 0 && mangaList.loadState.refresh is LoadState.Loading) {
  103. LoadingScreen(
  104. modifier = Modifier.padding(contentPadding),
  105. )
  106. return
  107. }
  108. when (displayMode) {
  109. LibraryDisplayMode.ComfortableGrid -> {
  110. BrowseSourceComfortableGrid(
  111. mangaList = mangaList,
  112. columns = columns,
  113. contentPadding = contentPadding,
  114. onMangaClick = onMangaClick,
  115. onMangaLongClick = onMangaLongClick,
  116. )
  117. }
  118. LibraryDisplayMode.List -> {
  119. BrowseSourceList(
  120. mangaList = mangaList,
  121. contentPadding = contentPadding,
  122. onMangaClick = onMangaClick,
  123. onMangaLongClick = onMangaLongClick,
  124. )
  125. }
  126. LibraryDisplayMode.CompactGrid, LibraryDisplayMode.CoverOnlyGrid -> {
  127. BrowseSourceCompactGrid(
  128. mangaList = mangaList,
  129. columns = columns,
  130. contentPadding = contentPadding,
  131. onMangaClick = onMangaClick,
  132. onMangaLongClick = onMangaLongClick,
  133. )
  134. }
  135. }
  136. }
  137. @Composable
  138. fun MissingSourceScreen(
  139. source: StubSource,
  140. navigateUp: () -> Unit,
  141. ) {
  142. Scaffold(
  143. topBar = { scrollBehavior ->
  144. AppBar(
  145. title = source.name,
  146. navigateUp = navigateUp,
  147. scrollBehavior = scrollBehavior,
  148. )
  149. },
  150. ) { paddingValues ->
  151. EmptyScreen(
  152. message = stringResource(R.string.source_not_installed, source.toString()),
  153. modifier = Modifier.padding(paddingValues),
  154. )
  155. }
  156. }