mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 14:27:57 +01:00 
			
		
		
		
	Misc cleanup
- Migrate sources filter screen to full compose - Use standard "OK"/"Cancel" actions for delete category dialog - Abstract some AppBar logic - Remove some dead code - Group related strings
This commit is contained in:
		| @@ -1,24 +1,27 @@ | ||||
| package eu.kanade.presentation.browse | ||||
|  | ||||
| import androidx.compose.foundation.layout.PaddingValues | ||||
| import androidx.compose.foundation.layout.WindowInsets | ||||
| import androidx.compose.foundation.layout.asPaddingValues | ||||
| import androidx.compose.foundation.layout.navigationBars | ||||
| import androidx.compose.foundation.layout.statusBarsPadding | ||||
| import androidx.compose.foundation.lazy.items | ||||
| import androidx.compose.material3.Checkbox | ||||
| import androidx.compose.material3.Switch | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.LaunchedEffect | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.input.nestedscroll.NestedScrollConnection | ||||
| import androidx.compose.ui.input.nestedscroll.nestedScroll | ||||
| import androidx.compose.ui.platform.LocalContext | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import eu.kanade.domain.source.model.Source | ||||
| import eu.kanade.presentation.browse.components.BaseSourceItem | ||||
| import eu.kanade.presentation.components.AppBar | ||||
| import eu.kanade.presentation.components.EmptyScreen | ||||
| import eu.kanade.presentation.components.LoadingScreen | ||||
| import eu.kanade.presentation.components.PreferenceRow | ||||
| import eu.kanade.presentation.components.Scaffold | ||||
| import eu.kanade.presentation.components.ScrollbarLazyColumn | ||||
| import eu.kanade.presentation.util.plus | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.ui.browse.source.FilterUiModel | ||||
| import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterPresenter | ||||
| @@ -28,22 +31,32 @@ import kotlinx.coroutines.flow.collectLatest | ||||
|  | ||||
| @Composable | ||||
| fun SourcesFilterScreen( | ||||
|     nestedScrollInterop: NestedScrollConnection, | ||||
|     navigateUp: () -> Unit, | ||||
|     presenter: SourcesFilterPresenter, | ||||
|     onClickLang: (String) -> Unit, | ||||
|     onClickSource: (Source) -> Unit, | ||||
| ) { | ||||
|     val context = LocalContext.current | ||||
|     when { | ||||
|         presenter.isLoading -> LoadingScreen() | ||||
|         presenter.isEmpty -> EmptyScreen(textResource = R.string.source_filter_empty_screen) | ||||
|         else -> { | ||||
|             SourcesFilterContent( | ||||
|                 nestedScrollInterop = nestedScrollInterop, | ||||
|                 state = presenter, | ||||
|                 onClickLang = onClickLang, | ||||
|                 onClickSource = onClickSource, | ||||
|     Scaffold( | ||||
|         modifier = Modifier.statusBarsPadding(), | ||||
|         topBar = { | ||||
|             AppBar( | ||||
|                 title = stringResource(R.string.label_sources), | ||||
|                 navigateUp = navigateUp, | ||||
|             ) | ||||
|         }, | ||||
|     ) { paddingValues -> | ||||
|         when { | ||||
|             presenter.isLoading -> LoadingScreen() | ||||
|             presenter.isEmpty -> EmptyScreen(textResource = R.string.source_filter_empty_screen) | ||||
|             else -> { | ||||
|                 SourcesFilterContent( | ||||
|                     paddingValues = paddingValues, | ||||
|                     state = presenter, | ||||
|                     onClickLang = onClickLang, | ||||
|                     onClickSource = onClickSource, | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     LaunchedEffect(Unit) { | ||||
| @@ -59,14 +72,13 @@ fun SourcesFilterScreen( | ||||
|  | ||||
| @Composable | ||||
| fun SourcesFilterContent( | ||||
|     nestedScrollInterop: NestedScrollConnection, | ||||
|     paddingValues: PaddingValues, | ||||
|     state: SourcesFilterState, | ||||
|     onClickLang: (String) -> Unit, | ||||
|     onClickSource: (Source) -> Unit, | ||||
| ) { | ||||
|     ScrollbarLazyColumn( | ||||
|         modifier = Modifier.nestedScroll(nestedScrollInterop), | ||||
|         contentPadding = WindowInsets.navigationBars.asPaddingValues(), | ||||
|         contentPadding = paddingValues + WindowInsets.navigationBars.asPaddingValues(), | ||||
|     ) { | ||||
|         items( | ||||
|             items = state.items, | ||||
|   | ||||
| @@ -118,16 +118,16 @@ fun CategoryDeleteDialog( | ||||
|     AlertDialog( | ||||
|         onDismissRequest = onDismissRequest, | ||||
|         confirmButton = { | ||||
|             TextButton(onClick = onDismissRequest) { | ||||
|                 Text(text = stringResource(R.string.no)) | ||||
|             } | ||||
|         }, | ||||
|         dismissButton = { | ||||
|             TextButton(onClick = { | ||||
|                 onDelete() | ||||
|                 onDismissRequest() | ||||
|             },) { | ||||
|                 Text(text = stringResource(R.string.yes)) | ||||
|                 Text(text = stringResource(android.R.string.ok)) | ||||
|             } | ||||
|         }, | ||||
|         dismissButton = { | ||||
|             TextButton(onClick = onDismissRequest) { | ||||
|                 Text(text = stringResource(android.R.string.cancel)) | ||||
|             } | ||||
|         }, | ||||
|         title = { | ||||
|   | ||||
| @@ -2,8 +2,14 @@ package eu.kanade.presentation.components | ||||
|  | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.RowScope | ||||
| import androidx.compose.foundation.layout.WindowInsets | ||||
| import androidx.compose.foundation.layout.WindowInsetsSides | ||||
| import androidx.compose.foundation.layout.only | ||||
| import androidx.compose.foundation.layout.systemBars | ||||
| import androidx.compose.foundation.layout.windowInsetsPadding | ||||
| import androidx.compose.material.icons.Icons | ||||
| import androidx.compose.material.icons.filled.ArrowBack | ||||
| import androidx.compose.material.icons.filled.Close | ||||
| import androidx.compose.material.icons.filled.MoreVert | ||||
| import androidx.compose.material3.DropdownMenuItem | ||||
| import androidx.compose.material3.Icon | ||||
| @@ -11,11 +17,16 @@ import androidx.compose.material3.IconButton | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.material3.SmallTopAppBar | ||||
| import androidx.compose.material3.Text | ||||
| import androidx.compose.material3.TopAppBarDefaults | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.derivedStateOf | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.runtime.setValue | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.draw.drawBehind | ||||
| import androidx.compose.ui.graphics.Color | ||||
| import androidx.compose.ui.graphics.vector.ImageVector | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import androidx.compose.ui.text.font.FontWeight | ||||
| @@ -24,29 +35,82 @@ import eu.kanade.tachiyomi.R | ||||
|  | ||||
| @Composable | ||||
| fun AppBar( | ||||
|     modifier: Modifier = Modifier, | ||||
|     // Text | ||||
|     title: String?, | ||||
|     subtitle: String? = null, | ||||
|     // Up button | ||||
|     navigateUp: (() -> Unit)? = null, | ||||
|     navigationIcon: ImageVector = Icons.Default.ArrowBack, | ||||
|     // Menu | ||||
|     actions: @Composable RowScope.() -> Unit = {}, | ||||
|     // Action mode | ||||
|     actionModeCounter: Int = 0, | ||||
|     onCancelActionMode: () -> Unit = {}, | ||||
|     actionModeActions: @Composable RowScope.() -> Unit = {}, | ||||
|     // Banners | ||||
|     downloadedOnlyMode: Boolean = false, | ||||
|     incognitoMode: Boolean = false, | ||||
| ) { | ||||
|     SmallTopAppBar( | ||||
|         navigationIcon = { | ||||
|             navigateUp?.let { | ||||
|                 IconButton(onClick = it) { | ||||
|                     Icon( | ||||
|                         imageVector = navigationIcon, | ||||
|                         contentDescription = stringResource(R.string.abc_action_bar_up_description), | ||||
|                     ) | ||||
|     val isActionMode by derivedStateOf { actionModeCounter > 0 } | ||||
|     val backgroundColor = if (isActionMode) { | ||||
|         TopAppBarDefaults.smallTopAppBarColors().containerColor(1f).value | ||||
|     } else { | ||||
|         MaterialTheme.colorScheme.surface | ||||
|     } | ||||
|  | ||||
|     Column( | ||||
|         modifier = modifier.drawBehind { drawRect(backgroundColor) }, | ||||
|     ) { | ||||
|         SmallTopAppBar( | ||||
|             modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Top)), | ||||
|             navigationIcon = { | ||||
|                 if (isActionMode) { | ||||
|                     IconButton(onClick = onCancelActionMode) { | ||||
|                         Icon( | ||||
|                             imageVector = Icons.Default.Close, | ||||
|                             contentDescription = stringResource(id = R.string.action_cancel), | ||||
|                         ) | ||||
|                     } | ||||
|                 } else { | ||||
|                     navigateUp?.let { | ||||
|                         IconButton(onClick = it) { | ||||
|                             Icon( | ||||
|                                 imageVector = navigationIcon, | ||||
|                                 contentDescription = stringResource(R.string.abc_action_bar_up_description), | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         title = { | ||||
|             AppBarTitle(title, subtitle) | ||||
|             // TODO: incognito/downloaded only banners | ||||
|         }, | ||||
|         actions = actions, | ||||
|     ) | ||||
|             }, | ||||
|             title = { | ||||
|                 if (isActionMode) { | ||||
|                     AppBarTitle(actionModeCounter.toString()) | ||||
|                 } else { | ||||
|                     AppBarTitle(title, subtitle) | ||||
|                 } | ||||
|             }, | ||||
|             actions = { | ||||
|                 if (isActionMode) { | ||||
|                     actionModeActions() | ||||
|                 } else { | ||||
|                     actions() | ||||
|                 } | ||||
|             }, | ||||
|             // Background handled by parent | ||||
|             colors = TopAppBarDefaults.smallTopAppBarColors( | ||||
|                 containerColor = Color.Transparent, | ||||
|                 scrolledContainerColor = Color.Transparent, | ||||
|             ), | ||||
|         ) | ||||
|  | ||||
|         if (downloadedOnlyMode) { | ||||
|             DownloadedOnlyModeBanner() | ||||
|         } | ||||
|         if (incognitoMode) { | ||||
|             IncognitoModeBanner() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @Composable | ||||
|   | ||||
| @@ -60,9 +60,9 @@ import eu.kanade.presentation.components.VerticalFastScroller | ||||
| import eu.kanade.presentation.manga.components.ChapterHeader | ||||
| import eu.kanade.presentation.manga.components.ExpandableMangaDescription | ||||
| import eu.kanade.presentation.manga.components.MangaActionRow | ||||
| import eu.kanade.presentation.manga.components.MangaAppBar | ||||
| import eu.kanade.presentation.manga.components.MangaChapterListItem | ||||
| import eu.kanade.presentation.manga.components.MangaInfoBox | ||||
| import eu.kanade.presentation.manga.components.MangaSmallAppBar | ||||
| import eu.kanade.presentation.util.isScrolledToEnd | ||||
| import eu.kanade.presentation.util.isScrollingUp | ||||
| import eu.kanade.presentation.util.plus | ||||
| @@ -237,7 +237,7 @@ private fun MangaScreenSmallImpl( | ||||
|             val animatedBgAlpha by animateFloatAsState( | ||||
|                 if (firstVisibleItemIndex > 0 || firstVisibleItemScrollOffset > 0) 1f else 0f, | ||||
|             ) | ||||
|             MangaSmallAppBar( | ||||
|             MangaAppBar( | ||||
|                 title = state.manga.title, | ||||
|                 titleAlphaProvider = { animatedTitleAlpha }, | ||||
|                 backgroundAlphaProvider = { animatedBgAlpha }, | ||||
| @@ -458,7 +458,7 @@ fun MangaScreenLargeImpl( | ||||
|         Scaffold( | ||||
|             modifier = Modifier.padding(insetPadding), | ||||
|             topBar = { | ||||
|                 MangaSmallAppBar( | ||||
|                 MangaAppBar( | ||||
|                     modifier = Modifier.onSizeChanged { onTopBarHeightChanged(it.height) }, | ||||
|                     title = state.manga.title, | ||||
|                     titleAlphaProvider = { if (chapters.any { it.selected }) 1f else 0f }, | ||||
|   | ||||
| @@ -38,7 +38,7 @@ import eu.kanade.presentation.manga.DownloadAction | ||||
| import eu.kanade.tachiyomi.R | ||||
| 
 | ||||
| @Composable | ||||
| fun MangaSmallAppBar( | ||||
| fun MangaAppBar( | ||||
|     modifier: Modifier = Modifier, | ||||
|     title: String, | ||||
|     titleAlphaProvider: () -> Float, | ||||
| @@ -57,7 +57,7 @@ fun MangaSmallAppBar( | ||||
| ) { | ||||
|     val isActionMode = actionModeCounter > 0 | ||||
|     val backgroundAlpha = if (isActionMode) 1f else backgroundAlphaProvider() | ||||
|     val backgroundColor by TopAppBarDefaults.centerAlignedTopAppBarColors().containerColor(1f) | ||||
|     val backgroundColor by TopAppBarDefaults.smallTopAppBarColors().containerColor(1f) | ||||
|     Column( | ||||
|         modifier = modifier.drawBehind { | ||||
|             drawRect(backgroundColor.copy(alpha = backgroundAlpha)) | ||||
| @@ -199,7 +199,7 @@ fun MangaSmallAppBar( | ||||
|                 } | ||||
|             }, | ||||
|             // Background handled by parent | ||||
|             colors = TopAppBarDefaults.centerAlignedTopAppBarColors( | ||||
|             colors = TopAppBarDefaults.smallTopAppBarColors( | ||||
|                 containerColor = Color.Transparent, | ||||
|                 scrolledContainerColor = Color.Transparent, | ||||
|             ), | ||||
| @@ -1,7 +1,6 @@ | ||||
| package eu.kanade.presentation.updates | ||||
|  | ||||
| import androidx.activity.compose.BackHandler | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.WindowInsets | ||||
| import androidx.compose.foundation.layout.WindowInsetsSides | ||||
| import androidx.compose.foundation.layout.asPaddingValues | ||||
| @@ -12,35 +11,25 @@ import androidx.compose.foundation.layout.navigationBars | ||||
| import androidx.compose.foundation.layout.only | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.layout.systemBars | ||||
| import androidx.compose.foundation.layout.windowInsetsPadding | ||||
| import androidx.compose.foundation.lazy.LazyColumn | ||||
| import androidx.compose.foundation.lazy.rememberLazyListState | ||||
| import androidx.compose.material.icons.Icons | ||||
| import androidx.compose.material.icons.filled.Close | ||||
| import androidx.compose.material.icons.filled.FlipToBack | ||||
| import androidx.compose.material.icons.filled.Refresh | ||||
| import androidx.compose.material.icons.filled.SelectAll | ||||
| import androidx.compose.material3.Icon | ||||
| import androidx.compose.material3.IconButton | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.material3.SmallTopAppBar | ||||
| import androidx.compose.material3.Text | ||||
| import androidx.compose.material3.TopAppBarDefaults | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.runtime.toMutableStateList | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.draw.drawBehind | ||||
| import androidx.compose.ui.graphics.Color | ||||
| import androidx.compose.ui.platform.LocalLayoutDirection | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import androidx.compose.ui.text.style.TextOverflow | ||||
| import com.google.accompanist.swiperefresh.SwipeRefresh | ||||
| import com.google.accompanist.swiperefresh.rememberSwipeRefreshState | ||||
| import eu.kanade.presentation.components.AppBar | ||||
| import eu.kanade.presentation.components.ChapterDownloadAction | ||||
| import eu.kanade.presentation.components.DownloadedOnlyModeBanner | ||||
| import eu.kanade.presentation.components.EmptyScreen | ||||
| import eu.kanade.presentation.components.IncognitoModeBanner | ||||
| import eu.kanade.presentation.components.MangaBottomActionMenu | ||||
| import eu.kanade.presentation.components.Scaffold | ||||
| import eu.kanade.presentation.components.SwipeRefreshIndicator | ||||
| @@ -201,72 +190,36 @@ fun UpdatesAppBar( | ||||
|     onSelectAll: () -> Unit, | ||||
|     onInvertSelection: () -> Unit, | ||||
| ) { | ||||
|     val isActionMode = actionModeCounter > 0 | ||||
|     val backgroundColor = if (isActionMode) { | ||||
|         TopAppBarDefaults.centerAlignedTopAppBarColors().containerColor(1f).value | ||||
|     } else { | ||||
|         MaterialTheme.colorScheme.surface | ||||
|     } | ||||
|  | ||||
|     Column( | ||||
|         modifier = modifier.drawBehind { drawRect(backgroundColor) }, | ||||
|     ) { | ||||
|         SmallTopAppBar( | ||||
|             modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Top)), | ||||
|             navigationIcon = { | ||||
|                 if (isActionMode) { | ||||
|                     IconButton(onClick = { selected.clear() }) { | ||||
|                         Icon( | ||||
|                             imageVector = Icons.Default.Close, | ||||
|                             contentDescription = stringResource(id = R.string.action_cancel), | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             title = { | ||||
|                 Text( | ||||
|                     text = if (isActionMode) actionModeCounter.toString() else stringResource(R.string.label_recent_updates), | ||||
|                     maxLines = 1, | ||||
|                     overflow = TextOverflow.Ellipsis, | ||||
|     AppBar( | ||||
|         modifier = modifier, | ||||
|         title = stringResource(R.string.label_recent_updates), | ||||
|         actions = { | ||||
|             IconButton(onClick = onUpdateLibrary) { | ||||
|                 Icon( | ||||
|                     imageVector = Icons.Default.Refresh, | ||||
|                     contentDescription = stringResource(R.string.action_update_library), | ||||
|                 ) | ||||
|             }, | ||||
|             actions = { | ||||
|                 if (isActionMode) { | ||||
|                     IconButton(onClick = onSelectAll) { | ||||
|                         Icon( | ||||
|                             imageVector = Icons.Default.SelectAll, | ||||
|                             contentDescription = stringResource(R.string.action_select_all), | ||||
|                         ) | ||||
|                     } | ||||
|                     IconButton(onClick = onInvertSelection) { | ||||
|                         Icon( | ||||
|                             imageVector = Icons.Default.FlipToBack, | ||||
|                             contentDescription = stringResource(R.string.action_select_inverse), | ||||
|                         ) | ||||
|                     } | ||||
|                 } else { | ||||
|                     IconButton(onClick = onUpdateLibrary) { | ||||
|                         Icon( | ||||
|                             imageVector = Icons.Default.Refresh, | ||||
|                             contentDescription = stringResource(R.string.action_update_library), | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             // Background handled by parent | ||||
|             colors = TopAppBarDefaults.centerAlignedTopAppBarColors( | ||||
|                 containerColor = Color.Transparent, | ||||
|                 scrolledContainerColor = Color.Transparent, | ||||
|             ), | ||||
|         ) | ||||
|  | ||||
|         if (downloadedOnlyMode) { | ||||
|             DownloadedOnlyModeBanner() | ||||
|         } | ||||
|         if (incognitoMode) { | ||||
|             IncognitoModeBanner() | ||||
|         } | ||||
|     } | ||||
|             } | ||||
|         }, | ||||
|         actionModeCounter = actionModeCounter, | ||||
|         onCancelActionMode = { selected.clear() }, | ||||
|         actionModeActions = { | ||||
|             IconButton(onClick = onSelectAll) { | ||||
|                 Icon( | ||||
|                     imageVector = Icons.Default.SelectAll, | ||||
|                     contentDescription = stringResource(R.string.action_select_all), | ||||
|                 ) | ||||
|             } | ||||
|             IconButton(onClick = onInvertSelection) { | ||||
|                 Icon( | ||||
|                     imageVector = Icons.Default.FlipToBack, | ||||
|                     contentDescription = stringResource(R.string.action_select_inverse), | ||||
|                 ) | ||||
|             } | ||||
|         }, | ||||
|         downloadedOnlyMode = downloadedOnlyMode, | ||||
|         incognitoMode = incognitoMode, | ||||
|     ) | ||||
| } | ||||
|  | ||||
| @Composable | ||||
|   | ||||
| @@ -1,22 +1,18 @@ | ||||
| package eu.kanade.tachiyomi.ui.browse.source | ||||
|  | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.input.nestedscroll.NestedScrollConnection | ||||
| import eu.kanade.domain.source.model.Source | ||||
| import eu.kanade.presentation.browse.SourcesFilterScreen | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.ui.base.controller.ComposeController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.FullComposeController | ||||
|  | ||||
| class SourceFilterController : ComposeController<SourcesFilterPresenter>() { | ||||
|  | ||||
|     override fun getTitle() = resources?.getString(R.string.label_sources) | ||||
| class SourceFilterController : FullComposeController<SourcesFilterPresenter>() { | ||||
|  | ||||
|     override fun createPresenter(): SourcesFilterPresenter = SourcesFilterPresenter() | ||||
|  | ||||
|     @Composable | ||||
|     override fun ComposeContent(nestedScrollInterop: NestedScrollConnection) { | ||||
|     override fun ComposeContent() { | ||||
|         SourcesFilterScreen( | ||||
|             nestedScrollInterop = nestedScrollInterop, | ||||
|             navigateUp = router::popCurrentController, | ||||
|             presenter = presenter, | ||||
|             onClickLang = { language -> | ||||
|                 presenter.toggleLanguage(language) | ||||
|   | ||||
| @@ -1,41 +0,0 @@ | ||||
| package eu.kanade.tachiyomi.ui.manga.chapter | ||||
|  | ||||
| import android.content.Context | ||||
| import android.util.AttributeSet | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.setValue | ||||
| import androidx.compose.ui.platform.AbstractComposeView | ||||
| import eu.kanade.presentation.components.ChapterDownloadAction | ||||
| import eu.kanade.presentation.components.ChapterDownloadIndicator | ||||
| import eu.kanade.presentation.theme.TachiyomiTheme | ||||
| import eu.kanade.tachiyomi.data.download.model.Download | ||||
|  | ||||
| class ChapterDownloadView @JvmOverloads constructor( | ||||
|     context: Context, | ||||
|     attrs: AttributeSet? = null, | ||||
|     defStyle: Int = 0, | ||||
| ) : AbstractComposeView(context, attrs, defStyle) { | ||||
|  | ||||
|     private var state by mutableStateOf(Download.State.NOT_DOWNLOADED) | ||||
|     private var progress by mutableStateOf(0) | ||||
|  | ||||
|     var listener: (ChapterDownloadAction) -> Unit = {} | ||||
|  | ||||
|     @Composable | ||||
|     override fun Content() { | ||||
|         TachiyomiTheme { | ||||
|             ChapterDownloadIndicator( | ||||
|                 downloadStateProvider = { state }, | ||||
|                 downloadProgressProvider = { progress }, | ||||
|                 onClick = listener, | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun setState(state: Download.State, progress: Int = 0) { | ||||
|         this.state = state | ||||
|         this.progress = progress | ||||
|     } | ||||
| } | ||||
| @@ -1,21 +0,0 @@ | ||||
| package eu.kanade.tachiyomi.ui.manga.chapter.base | ||||
|  | ||||
| import android.view.View | ||||
| import eu.davidea.viewholders.FlexibleViewHolder | ||||
| import eu.kanade.presentation.components.ChapterDownloadAction | ||||
|  | ||||
| open class BaseChapterHolder( | ||||
|     view: View, | ||||
|     private val adapter: BaseChaptersAdapter<*>, | ||||
| ) : FlexibleViewHolder(view, adapter) { | ||||
|  | ||||
|     val downloadActionListener: (ChapterDownloadAction) -> Unit = { action -> | ||||
|         when (action) { | ||||
|             ChapterDownloadAction.START -> adapter.clickListener.downloadChapter(bindingAdapterPosition) | ||||
|             ChapterDownloadAction.START_NOW -> adapter.clickListener.startDownloadNow(bindingAdapterPosition) | ||||
|             ChapterDownloadAction.CANCEL, ChapterDownloadAction.DELETE -> { | ||||
|                 adapter.clickListener.deleteChapter(bindingAdapterPosition) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,47 +0,0 @@ | ||||
| package eu.kanade.tachiyomi.ui.manga.chapter.base | ||||
|  | ||||
| import eu.davidea.flexibleadapter.items.AbstractHeaderItem | ||||
| import eu.davidea.flexibleadapter.items.AbstractSectionableItem | ||||
| import eu.kanade.domain.chapter.model.Chapter | ||||
| import eu.kanade.tachiyomi.data.download.model.Download | ||||
| import eu.kanade.tachiyomi.source.model.Page | ||||
|  | ||||
| abstract class BaseChapterItem<T : BaseChapterHolder, H : AbstractHeaderItem<*>>( | ||||
|     val chapter: Chapter, | ||||
|     header: H? = null, | ||||
| ) : AbstractSectionableItem<T, H?>(header) { | ||||
|  | ||||
|     private var _status: Download.State = Download.State.NOT_DOWNLOADED | ||||
|  | ||||
|     var status: Download.State | ||||
|         get() = download?.status ?: _status | ||||
|         set(value) { | ||||
|             _status = value | ||||
|         } | ||||
|  | ||||
|     val progress: Int | ||||
|         get() { | ||||
|             val pages = download?.pages ?: return 0 | ||||
|             return pages.map(Page::progress).average().toInt() | ||||
|         } | ||||
|  | ||||
|     @Transient | ||||
|     var download: Download? = null | ||||
|  | ||||
|     val isDownloaded: Boolean | ||||
|         get() = status == Download.State.DOWNLOADED | ||||
|  | ||||
|     override fun equals(other: Any?): Boolean { | ||||
|         if (this === other) return true | ||||
|         if (other is BaseChapterItem<*, *>) { | ||||
|             return chapter.id == other.chapter.id && chapter.read == other.chapter.read | ||||
|         } | ||||
|         return false | ||||
|     } | ||||
|  | ||||
|     override fun hashCode(): Int { | ||||
|         var result = chapter.id.hashCode() | ||||
|         result = 31 * result + chapter.read.hashCode() | ||||
|         return result | ||||
|     } | ||||
| } | ||||
| @@ -1,24 +0,0 @@ | ||||
| package eu.kanade.tachiyomi.ui.manga.chapter.base | ||||
|  | ||||
| import eu.davidea.flexibleadapter.FlexibleAdapter | ||||
| import eu.davidea.flexibleadapter.items.IFlexible | ||||
|  | ||||
| abstract class BaseChaptersAdapter<T : IFlexible<*>>( | ||||
|     controller: OnChapterClickListener, | ||||
|     items: List<T>? = null, | ||||
| ) : FlexibleAdapter<T>(items, controller, true) { | ||||
|  | ||||
|     /** | ||||
|      * Listener for browse item clicks. | ||||
|      */ | ||||
|     val clickListener: OnChapterClickListener = controller | ||||
|  | ||||
|     /** | ||||
|      * Listener which should be called when user clicks the download icons. | ||||
|      */ | ||||
|     interface OnChapterClickListener { | ||||
|         fun downloadChapter(position: Int) | ||||
|         fun deleteChapter(position: Int) | ||||
|         fun startDownloadNow(position: Int) | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user