Add "Open in browser" action in lists
This commit is contained in:
@@ -20,6 +20,7 @@ import coil.ImageLoader
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.browser.cloudflare.CaptchaNotifier
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package org.koitharu.kotatsu.list.ui.adapter
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.core.util.ext.setTextAndVisible
|
||||
import org.koitharu.kotatsu.databinding.ItemErrorStateBinding
|
||||
import org.koitharu.kotatsu.list.ui.model.ErrorState
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
@@ -13,10 +16,16 @@ fun errorStateListAD(
|
||||
{ inflater, parent -> ItemErrorStateBinding.inflate(inflater, parent, false) },
|
||||
) {
|
||||
|
||||
binding.buttonRetry.setOnClickListener {
|
||||
listener.onRetryClick(item.exception)
|
||||
val onClickListener = View.OnClickListener { v ->
|
||||
when (v.id) {
|
||||
R.id.button_retry -> listener.onRetryClick(item.exception)
|
||||
R.id.button_secondary -> listener.onSecondaryErrorActionClick(item.exception)
|
||||
}
|
||||
}
|
||||
|
||||
binding.buttonRetry.setOnClickListener(onClickListener)
|
||||
binding.buttonSecondary.setOnClickListener(onClickListener)
|
||||
|
||||
bind {
|
||||
with(binding.textViewError) {
|
||||
text = item.exception.getDisplayMessage(context.resources)
|
||||
@@ -26,5 +35,6 @@ fun errorStateListAD(
|
||||
isVisible = item.canRetry
|
||||
setText(item.buttonText)
|
||||
}
|
||||
binding.buttonSecondary.setTextAndVisible(item.secondaryButtonText)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package org.koitharu.kotatsu.list.ui.adapter
|
||||
interface ListStateHolderListener {
|
||||
|
||||
fun onRetryClick(error: Throwable)
|
||||
|
||||
|
||||
fun onSecondaryErrorActionClick(error: Throwable) = Unit
|
||||
|
||||
fun onEmptyActionClick()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ data class ErrorState(
|
||||
val exception: Throwable,
|
||||
@DrawableRes val icon: Int,
|
||||
val canRetry: Boolean,
|
||||
@StringRes val buttonText: Int
|
||||
@StringRes val buttonText: Int,
|
||||
@StringRes val secondaryButtonText: Int,
|
||||
) : ListModel {
|
||||
|
||||
override fun areItemsTheSame(other: ListModel) = other is ErrorState
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.koitharu.kotatsu.list.ui.model
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
@@ -74,11 +75,12 @@ suspend fun <C : MutableCollection<in MangaItemModel>> List<Manga>.toUi(
|
||||
ListMode.GRID -> mapTo(destination) { it.toGridModel(extraProvider) }
|
||||
}
|
||||
|
||||
fun Throwable.toErrorState(canRetry: Boolean = true) = ErrorState(
|
||||
fun Throwable.toErrorState(canRetry: Boolean = true, @StringRes secondaryAction: Int = 0) = ErrorState(
|
||||
exception = this,
|
||||
icon = getDisplayIcon(),
|
||||
canRetry = canRetry,
|
||||
buttonText = ExceptionResolver.getResolveStringId(this).ifZero { R.string.try_again },
|
||||
secondaryButtonText = secondaryAction,
|
||||
)
|
||||
|
||||
fun Throwable.toErrorFooter() = ErrorFooter(
|
||||
|
||||
@@ -10,10 +10,12 @@ import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.core.view.inputmethod.EditorInfoCompat
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.browser.BrowserActivity
|
||||
import org.koitharu.kotatsu.core.ui.list.ListSelectionController
|
||||
import org.koitharu.kotatsu.core.ui.util.MenuInvalidator
|
||||
import org.koitharu.kotatsu.core.util.ext.addMenuProvider
|
||||
@@ -70,6 +72,15 @@ class RemoteListFragment : MangaListFragment(), FilterOwner {
|
||||
viewModel.resetFilter()
|
||||
}
|
||||
|
||||
override fun onSecondaryErrorActionClick(error: Throwable) {
|
||||
viewModel.browserUrl?.also { url ->
|
||||
startActivity(
|
||||
BrowserActivity.newIntent(requireContext(), url, viewModel.source, viewModel.source.title),
|
||||
)
|
||||
} ?: Snackbar.make(requireViewBinding().recyclerView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
|
||||
private inner class RemoteListMenuProvider :
|
||||
MenuProvider,
|
||||
SearchView.OnQueryTextListener,
|
||||
|
||||
@@ -20,6 +20,7 @@ import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.distinctById
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
||||
import org.koitharu.kotatsu.core.util.ext.call
|
||||
@@ -43,6 +44,7 @@ import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaListFilter
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.parsers.util.concatUrl
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val FILTER_MIN_INTERVAL = 250L
|
||||
@@ -72,6 +74,9 @@ open class RemoteListViewModel @Inject constructor(
|
||||
val isSearchAvailable: Boolean
|
||||
get() = repository.isSearchSupported
|
||||
|
||||
val browserUrl: String?
|
||||
get() = (repository as? RemoteMangaRepository)?.domain?.let { "https://$it" }
|
||||
|
||||
override val content = combine(
|
||||
mangaList.map { it?.skipNsfwIfNeeded() },
|
||||
listMode,
|
||||
@@ -80,7 +85,13 @@ open class RemoteListViewModel @Inject constructor(
|
||||
) { list, mode, error, hasNext ->
|
||||
buildList(list?.size?.plus(2) ?: 2) {
|
||||
when {
|
||||
list.isNullOrEmpty() && error != null -> add(error.toErrorState(canRetry = true))
|
||||
list.isNullOrEmpty() && error != null -> add(
|
||||
error.toErrorState(
|
||||
canRetry = true,
|
||||
secondaryAction = if (browserUrl != null) R.string.open_in_browser else 0,
|
||||
),
|
||||
)
|
||||
|
||||
list == null -> add(LoadingState)
|
||||
list.isEmpty() -> add(createEmptyState(canResetFilter = header.value.isFilterApplied))
|
||||
else -> {
|
||||
|
||||
@@ -29,4 +29,13 @@
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/try_again" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_secondary"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
tools:text="@string/open_in_browser"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
Reference in New Issue
Block a user