LibrarySettingsDialog.kt 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. package eu.kanade.presentation.library
  2. import android.content.res.Configuration
  3. import androidx.compose.foundation.layout.Arrangement
  4. import androidx.compose.foundation.layout.Column
  5. import androidx.compose.foundation.layout.ColumnScope
  6. import androidx.compose.foundation.layout.Row
  7. import androidx.compose.foundation.layout.fillMaxWidth
  8. import androidx.compose.foundation.layout.padding
  9. import androidx.compose.foundation.rememberScrollState
  10. import androidx.compose.foundation.verticalScroll
  11. import androidx.compose.material3.MaterialTheme
  12. import androidx.compose.material3.Slider
  13. import androidx.compose.material3.Text
  14. import androidx.compose.runtime.Composable
  15. import androidx.compose.runtime.collectAsState
  16. import androidx.compose.runtime.getValue
  17. import androidx.compose.runtime.remember
  18. import androidx.compose.runtime.setValue
  19. import androidx.compose.ui.Alignment
  20. import androidx.compose.ui.Modifier
  21. import androidx.compose.ui.platform.LocalConfiguration
  22. import androidx.compose.ui.res.stringResource
  23. import androidx.compose.ui.unit.dp
  24. import eu.kanade.presentation.components.TabbedDialog
  25. import eu.kanade.presentation.components.TabbedDialogPaddings
  26. import eu.kanade.presentation.components.TriStateItem
  27. import eu.kanade.presentation.util.collectAsState
  28. import eu.kanade.tachiyomi.R
  29. import eu.kanade.tachiyomi.ui.library.LibrarySettingsScreenModel
  30. import kotlinx.coroutines.flow.map
  31. import tachiyomi.domain.category.model.Category
  32. import tachiyomi.domain.library.model.LibraryDisplayMode
  33. import tachiyomi.domain.library.model.LibrarySort
  34. import tachiyomi.domain.library.model.display
  35. import tachiyomi.domain.library.model.sort
  36. import tachiyomi.domain.library.service.LibraryPreferences
  37. import tachiyomi.domain.manga.model.TriStateFilter
  38. import tachiyomi.presentation.core.components.CheckboxItem
  39. import tachiyomi.presentation.core.components.HeadingItem
  40. import tachiyomi.presentation.core.components.RadioItem
  41. import tachiyomi.presentation.core.components.SettingsItemsPaddings
  42. import tachiyomi.presentation.core.components.SortItem
  43. @Composable
  44. fun LibrarySettingsDialog(
  45. onDismissRequest: () -> Unit,
  46. screenModel: LibrarySettingsScreenModel,
  47. category: Category,
  48. ) {
  49. TabbedDialog(
  50. onDismissRequest = onDismissRequest,
  51. tabTitles = listOf(
  52. stringResource(R.string.action_filter),
  53. stringResource(R.string.action_sort),
  54. stringResource(R.string.action_display),
  55. ),
  56. ) { page ->
  57. Column(
  58. modifier = Modifier
  59. .padding(vertical = TabbedDialogPaddings.Vertical)
  60. .verticalScroll(rememberScrollState()),
  61. ) {
  62. when (page) {
  63. 0 -> FilterPage(
  64. screenModel = screenModel,
  65. )
  66. 1 -> SortPage(
  67. category = category,
  68. screenModel = screenModel,
  69. )
  70. 2 -> DisplayPage(
  71. category = category,
  72. screenModel = screenModel,
  73. )
  74. }
  75. }
  76. }
  77. }
  78. @Composable
  79. private fun ColumnScope.FilterPage(
  80. screenModel: LibrarySettingsScreenModel,
  81. ) {
  82. val filterDownloaded by screenModel.libraryPreferences.filterDownloaded().collectAsState()
  83. val downloadedOnly by screenModel.preferences.downloadedOnly().collectAsState()
  84. TriStateItem(
  85. label = stringResource(R.string.label_downloaded),
  86. state = if (downloadedOnly) {
  87. TriStateFilter.ENABLED_IS
  88. } else {
  89. filterDownloaded
  90. },
  91. enabled = !downloadedOnly,
  92. onClick = { screenModel.toggleFilter(LibraryPreferences::filterDownloaded) },
  93. )
  94. val filterUnread by screenModel.libraryPreferences.filterUnread().collectAsState()
  95. TriStateItem(
  96. label = stringResource(R.string.action_filter_unread),
  97. state = filterUnread,
  98. onClick = { screenModel.toggleFilter(LibraryPreferences::filterUnread) },
  99. )
  100. val filterStarted by screenModel.libraryPreferences.filterStarted().collectAsState()
  101. TriStateItem(
  102. label = stringResource(R.string.label_started),
  103. state = filterStarted,
  104. onClick = { screenModel.toggleFilter(LibraryPreferences::filterStarted) },
  105. )
  106. val filterBookmarked by screenModel.libraryPreferences.filterBookmarked().collectAsState()
  107. TriStateItem(
  108. label = stringResource(R.string.action_filter_bookmarked),
  109. state = filterBookmarked,
  110. onClick = { screenModel.toggleFilter(LibraryPreferences::filterBookmarked) },
  111. )
  112. val filterCompleted by screenModel.libraryPreferences.filterCompleted().collectAsState()
  113. TriStateItem(
  114. label = stringResource(R.string.completed),
  115. state = filterCompleted,
  116. onClick = { screenModel.toggleFilter(LibraryPreferences::filterCompleted) },
  117. )
  118. val trackServices = remember { screenModel.trackServices }
  119. when (trackServices.size) {
  120. 0 -> {
  121. // No trackers
  122. }
  123. 1 -> {
  124. val service = trackServices[0]
  125. val filterTracker by screenModel.libraryPreferences.filterTracking(service.id.toInt()).collectAsState()
  126. TriStateItem(
  127. label = stringResource(R.string.action_filter_tracked),
  128. state = filterTracker,
  129. onClick = { screenModel.toggleTracker(service.id.toInt()) },
  130. )
  131. }
  132. else -> {
  133. HeadingItem(R.string.action_filter_tracked)
  134. trackServices.map { service ->
  135. val filterTracker by screenModel.libraryPreferences.filterTracking(service.id.toInt()).collectAsState()
  136. TriStateItem(
  137. label = stringResource(service.nameRes()),
  138. state = filterTracker,
  139. onClick = { screenModel.toggleTracker(service.id.toInt()) },
  140. )
  141. }
  142. }
  143. }
  144. }
  145. @Composable
  146. private fun ColumnScope.SortPage(
  147. category: Category,
  148. screenModel: LibrarySettingsScreenModel,
  149. ) {
  150. val sortingMode = category.sort.type
  151. val sortDescending = !category.sort.isAscending
  152. listOf(
  153. R.string.action_sort_alpha to LibrarySort.Type.Alphabetical,
  154. R.string.action_sort_total to LibrarySort.Type.TotalChapters,
  155. R.string.action_sort_last_read to LibrarySort.Type.LastRead,
  156. R.string.action_sort_last_manga_update to LibrarySort.Type.LastUpdate,
  157. R.string.action_sort_unread_count to LibrarySort.Type.UnreadCount,
  158. R.string.action_sort_latest_chapter to LibrarySort.Type.LatestChapter,
  159. R.string.action_sort_chapter_fetch_date to LibrarySort.Type.ChapterFetchDate,
  160. R.string.action_sort_date_added to LibrarySort.Type.DateAdded,
  161. ).map { (titleRes, mode) ->
  162. SortItem(
  163. label = stringResource(titleRes),
  164. sortDescending = sortDescending.takeIf { sortingMode == mode },
  165. onClick = {
  166. val isTogglingDirection = sortingMode == mode
  167. val direction = when {
  168. isTogglingDirection -> if (sortDescending) LibrarySort.Direction.Ascending else LibrarySort.Direction.Descending
  169. else -> if (sortDescending) LibrarySort.Direction.Descending else LibrarySort.Direction.Ascending
  170. }
  171. screenModel.setSort(category, mode, direction)
  172. },
  173. )
  174. }
  175. }
  176. @Composable
  177. private fun ColumnScope.DisplayPage(
  178. category: Category,
  179. screenModel: LibrarySettingsScreenModel,
  180. ) {
  181. HeadingItem(R.string.action_display_mode)
  182. listOf(
  183. R.string.action_display_grid to LibraryDisplayMode.CompactGrid,
  184. R.string.action_display_comfortable_grid to LibraryDisplayMode.ComfortableGrid,
  185. R.string.action_display_cover_only_grid to LibraryDisplayMode.CoverOnlyGrid,
  186. R.string.action_display_list to LibraryDisplayMode.List,
  187. ).map { (titleRes, mode) ->
  188. RadioItem(
  189. label = stringResource(titleRes),
  190. selected = category.display == mode,
  191. onClick = { screenModel.setDisplayMode(category, mode) },
  192. )
  193. }
  194. if (category.display != LibraryDisplayMode.List) {
  195. Row(
  196. modifier = Modifier
  197. .fillMaxWidth()
  198. .padding(
  199. horizontal = SettingsItemsPaddings.Horizontal,
  200. vertical = SettingsItemsPaddings.Vertical,
  201. ),
  202. verticalAlignment = Alignment.CenterVertically,
  203. horizontalArrangement = Arrangement.spacedBy(24.dp),
  204. ) {
  205. val configuration = LocalConfiguration.current
  206. val columnPreference = remember {
  207. if (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
  208. screenModel.libraryPreferences.landscapeColumns()
  209. } else {
  210. screenModel.libraryPreferences.portraitColumns()
  211. }
  212. }
  213. val columns by columnPreference.changes().collectAsState(initial = 0)
  214. Column(
  215. Modifier.weight(.5f),
  216. ) {
  217. Text(
  218. stringResource(id = R.string.pref_library_columns),
  219. style = MaterialTheme.typography.bodyMedium,
  220. )
  221. if (columns > 0) {
  222. Text(stringResource(id = R.string.pref_library_columns_per_row, columns))
  223. } else {
  224. Text(stringResource(id = R.string.label_default))
  225. }
  226. }
  227. Slider(
  228. value = columns.toFloat(),
  229. onValueChange = { columnPreference.set(it.toInt()) },
  230. modifier = Modifier.weight(1.5f),
  231. valueRange = 0f..10f,
  232. steps = 10,
  233. )
  234. }
  235. }
  236. HeadingItem(R.string.overlay_header)
  237. val downloadBadge by screenModel.libraryPreferences.downloadBadge().collectAsState()
  238. CheckboxItem(
  239. label = stringResource(R.string.action_display_download_badge),
  240. checked = downloadBadge,
  241. onClick = {
  242. screenModel.togglePreference(LibraryPreferences::downloadBadge)
  243. },
  244. )
  245. val localBadge by screenModel.libraryPreferences.localBadge().collectAsState()
  246. CheckboxItem(
  247. label = stringResource(R.string.action_display_local_badge),
  248. checked = localBadge,
  249. onClick = {
  250. screenModel.togglePreference(LibraryPreferences::localBadge)
  251. },
  252. )
  253. val languageBadge by screenModel.libraryPreferences.languageBadge().collectAsState()
  254. CheckboxItem(
  255. label = stringResource(R.string.action_display_language_badge),
  256. checked = languageBadge,
  257. onClick = {
  258. screenModel.togglePreference(LibraryPreferences::languageBadge)
  259. },
  260. )
  261. val showContinueReadingButton by screenModel.libraryPreferences.showContinueReadingButton().collectAsState()
  262. CheckboxItem(
  263. label = stringResource(R.string.action_display_show_continue_reading_button),
  264. checked = showContinueReadingButton,
  265. onClick = {
  266. screenModel.togglePreference(LibraryPreferences::showContinueReadingButton)
  267. },
  268. )
  269. HeadingItem(R.string.tabs_header)
  270. val categoryTabs by screenModel.libraryPreferences.categoryTabs().collectAsState()
  271. CheckboxItem(
  272. label = stringResource(R.string.action_display_show_tabs),
  273. checked = categoryTabs,
  274. onClick = {
  275. screenModel.togglePreference(LibraryPreferences::categoryTabs)
  276. },
  277. )
  278. val categoryNumberOfItems by screenModel.libraryPreferences.categoryNumberOfItems().collectAsState()
  279. CheckboxItem(
  280. label = stringResource(R.string.action_display_show_number_of_items),
  281. checked = categoryNumberOfItems,
  282. onClick = {
  283. screenModel.togglePreference(LibraryPreferences::categoryNumberOfItems)
  284. },
  285. )
  286. }