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.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.collections.immutable.persistentListOf
  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. persistentListOf(
  75. EmptyScreenAction(
  76. stringResId = R.string.local_source_help_guide,
  77. icon = Icons.Outlined.HelpOutline,
  78. onClick = onLocalSourceHelpClick,
  79. ),
  80. )
  81. } else {
  82. persistentListOf(
  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. }