MigrateSourceScreen.kt 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. package eu.kanade.presentation.browse
  2. import androidx.compose.foundation.background
  3. import androidx.compose.foundation.layout.Arrangement
  4. import androidx.compose.foundation.layout.Column
  5. import androidx.compose.foundation.layout.Row
  6. import androidx.compose.foundation.layout.WindowInsets
  7. import androidx.compose.foundation.layout.asPaddingValues
  8. import androidx.compose.foundation.layout.navigationBars
  9. import androidx.compose.foundation.layout.padding
  10. import androidx.compose.foundation.lazy.items
  11. import androidx.compose.material.icons.Icons
  12. import androidx.compose.material.icons.outlined.ArrowDownward
  13. import androidx.compose.material.icons.outlined.ArrowUpward
  14. import androidx.compose.material.icons.outlined.Numbers
  15. import androidx.compose.material.icons.outlined.SortByAlpha
  16. import androidx.compose.material3.Icon
  17. import androidx.compose.material3.IconButton
  18. import androidx.compose.material3.MaterialTheme
  19. import androidx.compose.material3.Text
  20. import androidx.compose.runtime.Composable
  21. import androidx.compose.ui.Alignment
  22. import androidx.compose.ui.Modifier
  23. import androidx.compose.ui.platform.LocalContext
  24. import androidx.compose.ui.res.stringResource
  25. import androidx.compose.ui.text.style.TextOverflow
  26. import androidx.compose.ui.unit.dp
  27. import eu.kanade.domain.source.interactor.SetMigrateSorting
  28. import eu.kanade.domain.source.model.Source
  29. import eu.kanade.presentation.browse.components.BaseSourceItem
  30. import eu.kanade.presentation.browse.components.SourceIcon
  31. import eu.kanade.presentation.components.Badge
  32. import eu.kanade.presentation.components.BadgeGroup
  33. import eu.kanade.presentation.components.EmptyScreen
  34. import eu.kanade.presentation.components.LoadingScreen
  35. import eu.kanade.presentation.components.ScrollbarLazyColumn
  36. import eu.kanade.presentation.theme.header
  37. import eu.kanade.presentation.util.bottomNavPaddingValues
  38. import eu.kanade.presentation.util.horizontalPadding
  39. import eu.kanade.presentation.util.plus
  40. import eu.kanade.presentation.util.topPaddingValues
  41. import eu.kanade.tachiyomi.R
  42. import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrationSourcesPresenter
  43. import eu.kanade.tachiyomi.util.system.LocaleHelper
  44. import eu.kanade.tachiyomi.util.system.copyToClipboard
  45. @Composable
  46. fun MigrateSourceScreen(
  47. presenter: MigrationSourcesPresenter,
  48. onClickItem: (Source) -> Unit,
  49. ) {
  50. val context = LocalContext.current
  51. when {
  52. presenter.isLoading -> LoadingScreen()
  53. presenter.isEmpty -> EmptyScreen(textResource = R.string.information_empty_library)
  54. else ->
  55. MigrateSourceList(
  56. list = presenter.items,
  57. onClickItem = onClickItem,
  58. onLongClickItem = { source ->
  59. val sourceId = source.id.toString()
  60. context.copyToClipboard(sourceId, sourceId)
  61. },
  62. sortingMode = presenter.sortingMode,
  63. onToggleSortingMode = { presenter.toggleSortingMode() },
  64. sortingDirection = presenter.sortingDirection,
  65. onToggleSortingDirection = { presenter.toggleSortingDirection() },
  66. )
  67. }
  68. }
  69. @Composable
  70. private fun MigrateSourceList(
  71. list: List<Pair<Source, Long>>,
  72. onClickItem: (Source) -> Unit,
  73. onLongClickItem: (Source) -> Unit,
  74. sortingMode: SetMigrateSorting.Mode,
  75. onToggleSortingMode: () -> Unit,
  76. sortingDirection: SetMigrateSorting.Direction,
  77. onToggleSortingDirection: () -> Unit,
  78. ) {
  79. ScrollbarLazyColumn(
  80. contentPadding = bottomNavPaddingValues + WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
  81. ) {
  82. stickyHeader(key = "header") {
  83. Row(
  84. modifier = Modifier
  85. .background(MaterialTheme.colorScheme.background)
  86. .padding(start = horizontalPadding),
  87. verticalAlignment = Alignment.CenterVertically,
  88. ) {
  89. Text(
  90. text = stringResource(R.string.migration_selection_prompt),
  91. modifier = Modifier.weight(1f),
  92. style = MaterialTheme.typography.header,
  93. )
  94. IconButton(onClick = onToggleSortingMode) {
  95. when (sortingMode) {
  96. SetMigrateSorting.Mode.ALPHABETICAL -> Icon(Icons.Outlined.SortByAlpha, contentDescription = stringResource(R.string.action_sort_alpha))
  97. SetMigrateSorting.Mode.TOTAL -> Icon(Icons.Outlined.Numbers, contentDescription = stringResource(R.string.action_sort_total))
  98. }
  99. }
  100. IconButton(onClick = onToggleSortingDirection) {
  101. when (sortingDirection) {
  102. SetMigrateSorting.Direction.ASCENDING -> Icon(Icons.Outlined.ArrowUpward, contentDescription = stringResource(R.string.action_asc))
  103. SetMigrateSorting.Direction.DESCENDING -> Icon(Icons.Outlined.ArrowDownward, contentDescription = stringResource(R.string.action_desc))
  104. }
  105. }
  106. }
  107. }
  108. items(
  109. items = list,
  110. key = { (source, _) -> source.id },
  111. ) { (source, count) ->
  112. MigrateSourceItem(
  113. modifier = Modifier.animateItemPlacement(),
  114. source = source,
  115. count = count,
  116. onClickItem = { onClickItem(source) },
  117. onLongClickItem = { onLongClickItem(source) },
  118. )
  119. }
  120. }
  121. }
  122. @Composable
  123. private fun MigrateSourceItem(
  124. modifier: Modifier = Modifier,
  125. source: Source,
  126. count: Long,
  127. onClickItem: () -> Unit,
  128. onLongClickItem: () -> Unit,
  129. ) {
  130. BaseSourceItem(
  131. modifier = modifier,
  132. source = source,
  133. showLanguageInContent = source.lang != "",
  134. onClickItem = onClickItem,
  135. onLongClickItem = onLongClickItem,
  136. icon = { SourceIcon(source = source) },
  137. action = {
  138. BadgeGroup {
  139. Badge(text = "$count")
  140. }
  141. },
  142. content = { source, showLanguageInContent ->
  143. Column(
  144. modifier = Modifier
  145. .padding(horizontal = horizontalPadding)
  146. .weight(1f),
  147. ) {
  148. Text(
  149. text = source.name.ifBlank { source.id.toString() },
  150. maxLines = 1,
  151. overflow = TextOverflow.Ellipsis,
  152. style = MaterialTheme.typography.bodyMedium,
  153. )
  154. Row(
  155. horizontalArrangement = Arrangement.spacedBy(8.dp),
  156. verticalAlignment = Alignment.CenterVertically,
  157. ) {
  158. if (showLanguageInContent) {
  159. Text(
  160. text = LocaleHelper.getDisplayName(source.lang),
  161. maxLines = 1,
  162. overflow = TextOverflow.Ellipsis,
  163. style = MaterialTheme.typography.bodySmall,
  164. )
  165. }
  166. if (source.isStub) {
  167. Text(
  168. text = stringResource(R.string.not_installed),
  169. maxLines = 1,
  170. overflow = TextOverflow.Ellipsis,
  171. style = MaterialTheme.typography.bodySmall,
  172. color = MaterialTheme.colorScheme.error,
  173. )
  174. }
  175. }
  176. }
  177. },
  178. )
  179. }