BrowseSourceScreen.kt 6.1 KB

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