mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
refactor: Replace LoadingButtonAndroid with compose
REF: https://gist.github.com/mmolosay/584ce5c47567cb66228b76ef98c3c4e4
This commit is contained in:
parent
61e43e047f
commit
6f94dd091b
9 changed files with 228 additions and 26 deletions
|
@ -235,7 +235,6 @@ dependencies {
|
|||
implementation(libs.aboutlibraries)
|
||||
|
||||
// UI
|
||||
implementation(libs.loading.button)
|
||||
implementation(libs.fastadapter)
|
||||
implementation(libs.fastadapter.extensions.binding)
|
||||
implementation(libs.flexible.adapter)
|
||||
|
|
|
@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.widget.preference
|
|||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.StringRes
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
|
@ -53,7 +52,7 @@ abstract class LoginDialogPreference(
|
|||
binding.usernameInput.hint = view.context.getString(usernameLabelRes)
|
||||
}
|
||||
|
||||
binding.login.setOnClickListener {
|
||||
binding.login.setOnClick {
|
||||
checkLogin()
|
||||
}
|
||||
|
||||
|
|
|
@ -2,13 +2,7 @@ package eu.kanade.tachiyomi.widget.preference
|
|||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.StringRes
|
||||
import br.com.simplepass.loadingbutton.animatedDrawables.ProgressType
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.tachiyomi.R
|
||||
import yokai.i18n.MR
|
||||
import yokai.util.lang.getString
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
|
@ -16,6 +10,8 @@ import eu.kanade.tachiyomi.util.system.withIOContext
|
|||
import kotlinx.coroutines.launch
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import yokai.i18n.MR
|
||||
import yokai.util.lang.getString
|
||||
|
||||
class TrackLoginDialog(usernameLabelRes: StringResource? = null, bundle: Bundle? = null) :
|
||||
LoginDialogPreference(usernameLabelRes, bundle) {
|
||||
|
@ -36,10 +32,7 @@ class TrackLoginDialog(usernameLabelRes: StringResource? = null, bundle: Bundle?
|
|||
|
||||
override fun checkLogin() {
|
||||
v?.apply {
|
||||
binding.login.apply {
|
||||
progressType = ProgressType.INDETERMINATE
|
||||
startAnimation()
|
||||
}
|
||||
binding.login.startAnimation()
|
||||
if (binding.username.text.isNullOrBlank() || binding.password.text.isNullOrBlank()) {
|
||||
errorResult()
|
||||
context.toast(MR.strings.username_must_not_be_blank)
|
||||
|
|
154
app/src/main/java/yokai/presentation/component/LoadingButton.kt
Normal file
154
app/src/main/java/yokai/presentation/component/LoadingButton.kt
Normal file
|
@ -0,0 +1,154 @@
|
|||
package yokai.presentation.component
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.Transition
|
||||
import androidx.compose.animation.core.VisibilityThreshold
|
||||
import androidx.compose.animation.core.animateDp
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.animation.expandHorizontally
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkHorizontally
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// REF: https://gist.github.com/mmolosay/584ce5c47567cb66228b76ef98c3c4e4
|
||||
|
||||
private val SpringStiffness = Spring.StiffnessMediumLow
|
||||
|
||||
@Composable
|
||||
fun LoadingButton(
|
||||
text: () -> String,
|
||||
loading: () -> Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val transition = updateTransition(
|
||||
targetState = loading(),
|
||||
label = "master transition",
|
||||
)
|
||||
val horizontalContentPadding by transition.animateDp(
|
||||
transitionSpec = {
|
||||
spring(
|
||||
stiffness = SpringStiffness,
|
||||
)
|
||||
},
|
||||
targetValueByState = { toLoading -> if (toLoading) 12.dp else 24.dp },
|
||||
label = "button's content padding",
|
||||
)
|
||||
Button(
|
||||
onClick = onClick,
|
||||
modifier = modifier.defaultMinSize(minWidth = 1.dp),
|
||||
contentPadding = PaddingValues(
|
||||
horizontal = horizontalContentPadding,
|
||||
vertical = 8.dp,
|
||||
),
|
||||
) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
LoadingContent(
|
||||
loadingStateTransition = transition,
|
||||
)
|
||||
PrimaryContent(
|
||||
text = text(),
|
||||
loadingStateTransition = transition,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LoadingContent(
|
||||
loadingStateTransition: Transition<Boolean>,
|
||||
) {
|
||||
loadingStateTransition.AnimatedVisibility(
|
||||
visible = { loading -> loading },
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(
|
||||
animationSpec = spring(
|
||||
stiffness = SpringStiffness,
|
||||
visibilityThreshold = 0.10f,
|
||||
),
|
||||
),
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(18.dp),
|
||||
color = LocalContentColor.current,
|
||||
strokeWidth = 1.5f.dp,
|
||||
strokeCap = StrokeCap.Round,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PrimaryContent(
|
||||
text: String,
|
||||
loadingStateTransition: Transition<Boolean>,
|
||||
) {
|
||||
loadingStateTransition.AnimatedVisibility(
|
||||
visible = { loading -> !loading },
|
||||
enter = fadeIn() + expandHorizontally(
|
||||
animationSpec = spring(
|
||||
stiffness = SpringStiffness,
|
||||
dampingRatio = Spring.DampingRatioMediumBouncy,
|
||||
visibilityThreshold = IntSize.VisibilityThreshold,
|
||||
),
|
||||
expandFrom = Alignment.CenterHorizontally,
|
||||
),
|
||||
exit = fadeOut(
|
||||
animationSpec = spring(
|
||||
stiffness = SpringStiffness,
|
||||
visibilityThreshold = 0.10f,
|
||||
),
|
||||
) + shrinkHorizontally(
|
||||
animationSpec = spring(
|
||||
stiffness = SpringStiffness,
|
||||
// dampingRatio is not applicable here, size cannot become negative
|
||||
visibilityThreshold = IntSize.VisibilityThreshold,
|
||||
),
|
||||
shrinkTowards = Alignment.CenterHorizontally,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
modifier = Modifier
|
||||
// so that bouncing button's width doesn't cut first and last letters
|
||||
.padding(horizontal = 4.dp),
|
||||
fontSize = 16.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun LoadingButtonPreview() {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
LoadingButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = { "Test" },
|
||||
loading = { false },
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package yokai.presentation.component
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.widget.FrameLayout
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.AbstractComposeView
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import eu.kanade.tachiyomi.R
|
||||
import yokai.presentation.theme.YokaiTheme
|
||||
|
||||
class LoadingButtonComposeView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
) : AbstractComposeView(context, attrs, defStyleAttr) {
|
||||
|
||||
var text by mutableStateOf("placeholder")
|
||||
private var onClick: () -> Unit = {}
|
||||
private var isLoading by mutableStateOf(false)
|
||||
|
||||
init {
|
||||
layoutParams = FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER)
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindowOrReleasedFromPool)
|
||||
attrs?.let {
|
||||
val arr = context.obtainStyledAttributes(it, R.styleable.LoadingButtonComposeView)
|
||||
text = context.getString(arr.getResourceId(R.styleable.LoadingButtonComposeView_android_text, R.string.log_in))
|
||||
}
|
||||
}
|
||||
|
||||
fun setOnClick(onClick: () -> Unit) {
|
||||
this.onClick = onClick
|
||||
}
|
||||
|
||||
fun startAnimation() {
|
||||
isLoading = true
|
||||
}
|
||||
|
||||
fun revertAnimation(after: () -> Unit = {}) {
|
||||
isLoading = false
|
||||
after()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
YokaiTheme {
|
||||
LoadingButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = { text },
|
||||
loading = { isLoading },
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -49,21 +49,12 @@
|
|||
android:inputType="textPassword" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
|
||||
<br.com.simplepass.loadingbutton.customViews.CircularProgressButton
|
||||
<yokai.presentation.component.LoadingButtonComposeView
|
||||
android:id="@+id/login"
|
||||
style="@style/Widget.Tachiyomi.Button.Primary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="55dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="20dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:text="@string/log_in"
|
||||
android:textSize="16sp"
|
||||
app:finalCornerAngle="50dp"
|
||||
app:initialCornerAngle="2dp"
|
||||
app:spinning_bar_color="@color/md_white_1000"
|
||||
app:spinning_bar_padding="6dp"
|
||||
app:spinning_bar_width="3dp" />
|
||||
android:text="@string/log_in" />
|
||||
|
||||
</LinearLayout>
|
|
@ -49,6 +49,10 @@
|
|||
<attr name="endIcon" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="LoadingButtonComposeView">
|
||||
<attr name="android:text" />
|
||||
</declare-styleable>
|
||||
|
||||
<bool name="isLightMode">true</bool>
|
||||
|
||||
<fraction name="chartRatio">1</fraction>
|
||||
|
|
|
@ -100,6 +100,8 @@
|
|||
<color name="pale_green">#99CC99</color>
|
||||
<color name="strong_green">#106010</color>
|
||||
|
||||
<color name="black">#000</color>
|
||||
|
||||
<!-- Navigation overlay colors -->
|
||||
<color name="navigation_menu">#CC95818D</color>
|
||||
<color name="navigation_next">#CCA6CFD5</color>
|
||||
|
|
|
@ -57,7 +57,6 @@ jsoup = { module = "org.jsoup:jsoup", version = "1.17.1" }
|
|||
junit-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" }
|
||||
junit-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" }
|
||||
junit-android = { module = "androidx.test.ext:junit", version = "1.1.5" }
|
||||
loading-button = { module = "com.github.leandroBorgesFerreira:LoadingButtonAndroid", version = "2.2.0" } # FIXME: Don't depends on this
|
||||
mockk = { module = "io.mockk:mockk", version = "1.13.11" }
|
||||
|
||||
moko-resources = { module = "dev.icerock.moko:resources", version.ref = "moko" }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue