UpdatesUiItem.kt 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. package eu.kanade.presentation.updates
  2. import androidx.compose.foundation.background
  3. import androidx.compose.foundation.combinedClickable
  4. import androidx.compose.foundation.layout.Column
  5. import androidx.compose.foundation.layout.Row
  6. import androidx.compose.foundation.layout.Spacer
  7. import androidx.compose.foundation.layout.fillMaxHeight
  8. import androidx.compose.foundation.layout.height
  9. import androidx.compose.foundation.layout.padding
  10. import androidx.compose.foundation.layout.sizeIn
  11. import androidx.compose.foundation.layout.width
  12. import androidx.compose.foundation.lazy.LazyListScope
  13. import androidx.compose.foundation.lazy.items
  14. import androidx.compose.material.icons.Icons
  15. import androidx.compose.material.icons.filled.Bookmark
  16. import androidx.compose.material3.Icon
  17. import androidx.compose.material3.MaterialTheme
  18. import androidx.compose.material3.Text
  19. import androidx.compose.runtime.Composable
  20. import androidx.compose.runtime.getValue
  21. import androidx.compose.runtime.mutableStateOf
  22. import androidx.compose.runtime.remember
  23. import androidx.compose.runtime.setValue
  24. import androidx.compose.ui.Alignment
  25. import androidx.compose.ui.Modifier
  26. import androidx.compose.ui.draw.alpha
  27. import androidx.compose.ui.graphics.Color
  28. import androidx.compose.ui.hapticfeedback.HapticFeedbackType
  29. import androidx.compose.ui.platform.LocalDensity
  30. import androidx.compose.ui.platform.LocalHapticFeedback
  31. import androidx.compose.ui.res.stringResource
  32. import androidx.compose.ui.text.style.TextOverflow
  33. import androidx.compose.ui.unit.dp
  34. import eu.kanade.domain.updates.model.UpdatesWithRelations
  35. import eu.kanade.presentation.components.ChapterDownloadAction
  36. import eu.kanade.presentation.components.ChapterDownloadIndicator
  37. import eu.kanade.presentation.components.MangaCover
  38. import eu.kanade.presentation.components.RelativeDateHeader
  39. import eu.kanade.presentation.util.ReadItemAlpha
  40. import eu.kanade.presentation.util.horizontalPadding
  41. import eu.kanade.tachiyomi.R
  42. import eu.kanade.tachiyomi.data.download.model.Download
  43. import eu.kanade.tachiyomi.ui.recent.updates.UpdatesItem
  44. import java.text.DateFormat
  45. fun LazyListScope.updatesUiItems(
  46. uiModels: List<UpdatesUiModel>,
  47. selectionMode: Boolean,
  48. onUpdateSelected: (UpdatesItem, Boolean, Boolean, Boolean) -> Unit,
  49. onClickCover: (UpdatesItem) -> Unit,
  50. onClickUpdate: (UpdatesItem) -> Unit,
  51. onDownloadChapter: (List<UpdatesItem>, ChapterDownloadAction) -> Unit,
  52. relativeTime: Int,
  53. dateFormat: DateFormat,
  54. ) {
  55. items(
  56. items = uiModels,
  57. contentType = {
  58. when (it) {
  59. is UpdatesUiModel.Header -> "header"
  60. is UpdatesUiModel.Item -> "item"
  61. }
  62. },
  63. key = {
  64. when (it) {
  65. is UpdatesUiModel.Header -> it.hashCode()
  66. is UpdatesUiModel.Item -> it.item.update.chapterId
  67. }
  68. },
  69. ) { item ->
  70. when (item) {
  71. is UpdatesUiModel.Header -> {
  72. RelativeDateHeader(
  73. modifier = Modifier.animateItemPlacement(),
  74. date = item.date,
  75. relativeTime = relativeTime,
  76. dateFormat = dateFormat,
  77. )
  78. }
  79. is UpdatesUiModel.Item -> {
  80. val updatesItem = item.item
  81. val update = updatesItem.update
  82. UpdatesUiItem(
  83. modifier = Modifier.animateItemPlacement(),
  84. update = update,
  85. selected = updatesItem.selected,
  86. onLongClick = {
  87. onUpdateSelected(updatesItem, !updatesItem.selected, true, true)
  88. },
  89. onClick = {
  90. when {
  91. selectionMode -> onUpdateSelected(updatesItem, !updatesItem.selected, true, false)
  92. else -> onClickUpdate(updatesItem)
  93. }
  94. },
  95. onClickCover = { if (selectionMode.not()) onClickCover(updatesItem) },
  96. onDownloadChapter = {
  97. if (selectionMode.not()) onDownloadChapter(listOf(updatesItem), it)
  98. },
  99. downloadStateProvider = updatesItem.downloadStateProvider,
  100. downloadProgressProvider = updatesItem.downloadProgressProvider,
  101. )
  102. }
  103. }
  104. }
  105. }
  106. @Composable
  107. fun UpdatesUiItem(
  108. modifier: Modifier,
  109. update: UpdatesWithRelations,
  110. selected: Boolean,
  111. onClick: () -> Unit,
  112. onLongClick: () -> Unit,
  113. onClickCover: () -> Unit,
  114. onDownloadChapter: (ChapterDownloadAction) -> Unit,
  115. // Download Indicator
  116. downloadStateProvider: () -> Download.State,
  117. downloadProgressProvider: () -> Int,
  118. ) {
  119. val haptic = LocalHapticFeedback.current
  120. Row(
  121. modifier = modifier
  122. .background(if (selected) MaterialTheme.colorScheme.surfaceVariant else Color.Transparent)
  123. .combinedClickable(
  124. onClick = onClick,
  125. onLongClick = {
  126. onLongClick()
  127. haptic.performHapticFeedback(HapticFeedbackType.LongPress)
  128. },
  129. )
  130. .height(56.dp)
  131. .padding(horizontal = horizontalPadding),
  132. verticalAlignment = Alignment.CenterVertically,
  133. ) {
  134. MangaCover.Square(
  135. modifier = Modifier
  136. .padding(vertical = 6.dp)
  137. .fillMaxHeight(),
  138. data = update.coverData,
  139. onClick = onClickCover,
  140. )
  141. Column(
  142. modifier = Modifier
  143. .padding(horizontal = horizontalPadding)
  144. .weight(1f),
  145. ) {
  146. val bookmark = remember(update.bookmark) { update.bookmark }
  147. val read = remember(update.read) { update.read }
  148. val textAlpha = remember(read) { if (read) ReadItemAlpha else 1f }
  149. val secondaryTextColor = if (bookmark && !read) {
  150. MaterialTheme.colorScheme.primary
  151. } else {
  152. MaterialTheme.colorScheme.onSurface
  153. }
  154. Text(
  155. text = update.mangaTitle,
  156. maxLines = 1,
  157. style = MaterialTheme.typography.bodyMedium,
  158. overflow = TextOverflow.Ellipsis,
  159. modifier = Modifier.alpha(textAlpha),
  160. )
  161. Row(verticalAlignment = Alignment.CenterVertically) {
  162. var textHeight by remember { mutableStateOf(0) }
  163. if (bookmark) {
  164. Icon(
  165. imageVector = Icons.Default.Bookmark,
  166. contentDescription = stringResource(R.string.action_filter_bookmarked),
  167. modifier = Modifier
  168. .sizeIn(maxHeight = with(LocalDensity.current) { textHeight.toDp() - 2.dp }),
  169. tint = MaterialTheme.colorScheme.primary,
  170. )
  171. Spacer(modifier = Modifier.width(2.dp))
  172. }
  173. Text(
  174. text = update.chapterName,
  175. maxLines = 1,
  176. style = MaterialTheme.typography.bodySmall
  177. .copy(color = secondaryTextColor),
  178. overflow = TextOverflow.Ellipsis,
  179. onTextLayout = { textHeight = it.size.height },
  180. modifier = Modifier.alpha(textAlpha),
  181. )
  182. }
  183. }
  184. ChapterDownloadIndicator(
  185. modifier = Modifier.padding(start = 4.dp),
  186. downloadStateProvider = downloadStateProvider,
  187. downloadProgressProvider = downloadProgressProvider,
  188. onClick = onDownloadChapter,
  189. )
  190. }
  191. }