MigrateSourceScreen.kt 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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.text.style.TextOverflow
  23. import eu.kanade.domain.source.interactor.SetMigrateSorting
  24. import eu.kanade.presentation.browse.components.BaseSourceItem
  25. import eu.kanade.presentation.browse.components.SourceIcon
  26. import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrateSourceScreenModel
  27. import eu.kanade.tachiyomi.util.system.copyToClipboard
  28. import tachiyomi.domain.source.model.Source
  29. import tachiyomi.i18n.MR
  30. import tachiyomi.presentation.core.components.Badge
  31. import tachiyomi.presentation.core.components.BadgeGroup
  32. import tachiyomi.presentation.core.components.ScrollbarLazyColumn
  33. import tachiyomi.presentation.core.components.Scroller.STICKY_HEADER_KEY_PREFIX
  34. import tachiyomi.presentation.core.components.material.padding
  35. import tachiyomi.presentation.core.components.material.topSmallPaddingValues
  36. import tachiyomi.presentation.core.i18n.stringResource
  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. stringRes = MR.strings.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(MR.strings.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(MR.strings.action_sort_alpha),
  104. )
  105. SetMigrateSorting.Mode.TOTAL -> Icon(
  106. Icons.Outlined.Numbers,
  107. contentDescription = stringResource(MR.strings.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(MR.strings.action_asc),
  116. )
  117. SetMigrateSorting.Direction.DESCENDING -> Icon(
  118. Icons.Outlined.ArrowDownward,
  119. contentDescription = stringResource(MR.strings.action_desc),
  120. )
  121. }
  122. }
  123. }
  124. }
  125. items(
  126. items = list,
  127. key = { (source, _) -> "migrate-${source.id}" },
  128. ) { (source, count) ->
  129. MigrateSourceItem(
  130. source = source,
  131. count = count,
  132. onClickItem = { onClickItem(source) },
  133. onLongClickItem = { onLongClickItem(source) },
  134. )
  135. }
  136. }
  137. }
  138. @Composable
  139. private fun MigrateSourceItem(
  140. source: Source,
  141. count: Long,
  142. onClickItem: () -> Unit,
  143. onLongClickItem: () -> Unit,
  144. modifier: Modifier = Modifier,
  145. ) {
  146. BaseSourceItem(
  147. modifier = modifier,
  148. source = source,
  149. showLanguageInContent = source.lang != "",
  150. onClickItem = onClickItem,
  151. onLongClickItem = onLongClickItem,
  152. icon = { SourceIcon(source = source) },
  153. action = {
  154. BadgeGroup {
  155. Badge(text = "$count")
  156. }
  157. },
  158. content = { _, sourceLangString ->
  159. Column(
  160. modifier = Modifier
  161. .padding(horizontal = MaterialTheme.padding.medium)
  162. .weight(1f),
  163. ) {
  164. Text(
  165. text = source.name.ifBlank { source.id.toString() },
  166. maxLines = 1,
  167. overflow = TextOverflow.Ellipsis,
  168. style = MaterialTheme.typography.bodyMedium,
  169. )
  170. Row(
  171. horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
  172. verticalAlignment = Alignment.CenterVertically,
  173. ) {
  174. if (sourceLangString != null) {
  175. Text(
  176. modifier = Modifier.secondaryItemAlpha(),
  177. text = sourceLangString,
  178. maxLines = 1,
  179. overflow = TextOverflow.Ellipsis,
  180. style = MaterialTheme.typography.bodySmall,
  181. )
  182. }
  183. if (source.isStub) {
  184. Text(
  185. modifier = Modifier.secondaryItemAlpha(),
  186. text = stringResource(MR.strings.not_installed),
  187. maxLines = 1,
  188. overflow = TextOverflow.Ellipsis,
  189. style = MaterialTheme.typography.bodySmall,
  190. color = MaterialTheme.colorScheme.error,
  191. )
  192. }
  193. }
  194. }
  195. },
  196. )
  197. }