MigrateSourceScreen.kt 8.1 KB

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