Adding pull to refresh on Bet Screen
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
6104245af7
commit
7dfe35caeb
@ -0,0 +1,38 @@
|
|||||||
|
package fr.iut.alldev.allin.ui.bet
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class BetViewModel @Inject constructor(
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val _isRefreshing = MutableStateFlow(false)
|
||||||
|
val isRefreshing: StateFlow<Boolean>
|
||||||
|
get() = _isRefreshing.asStateFlow()
|
||||||
|
|
||||||
|
|
||||||
|
private fun refreshData(){
|
||||||
|
Thread.sleep(1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun refresh() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_isRefreshing.emit(true)
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
refreshData()
|
||||||
|
}
|
||||||
|
_isRefreshing.emit(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,209 @@
|
|||||||
|
package fr.iut.alldev.allin.ui.core
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.animation.core.*
|
||||||
|
import androidx.compose.foundation.Canvas
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.progressSemantics
|
||||||
|
import androidx.compose.material3.ProgressIndicatorDefaults
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.geometry.Size
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
|
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||||
|
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import fr.iut.alldev.allin.ui.theme.AllInTheme
|
||||||
|
import kotlin.math.PI
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AllInLoading(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
brush: Brush = AllInTheme.colors.allIn_MainGradient
|
||||||
|
) {
|
||||||
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.clickable(
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
indication = null,
|
||||||
|
onClick = {}
|
||||||
|
)
|
||||||
|
.background(AllInTheme.themeColors.main_surface.copy(alpha = .4f))
|
||||||
|
) {
|
||||||
|
AllInCircularProgressIndicator(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.Center)
|
||||||
|
.size(50.dp),
|
||||||
|
brush = brush,
|
||||||
|
strokeWidth = 7.dp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AllInCircularProgressIndicator(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
brush: Brush = AllInTheme.colors.allIn_MainGradient,
|
||||||
|
strokeWidth: Dp = ProgressIndicatorDefaults.CircularStrokeWidth,
|
||||||
|
strokeCap: StrokeCap = ProgressIndicatorDefaults.CircularIndeterminateStrokeCap,
|
||||||
|
) {
|
||||||
|
val stroke = with(LocalDensity.current) {
|
||||||
|
Stroke(width = strokeWidth.toPx(), cap = strokeCap)
|
||||||
|
}
|
||||||
|
|
||||||
|
val transition = rememberInfiniteTransition()
|
||||||
|
val currentRotation = transition.animateValue(
|
||||||
|
0,
|
||||||
|
RotationsPerCycle,
|
||||||
|
Int.VectorConverter,
|
||||||
|
infiniteRepeatable(
|
||||||
|
animation = tween(
|
||||||
|
durationMillis = RotationDuration * RotationsPerCycle,
|
||||||
|
easing = LinearEasing
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val baseRotation = transition.animateFloat(
|
||||||
|
0f,
|
||||||
|
BaseRotationAngle,
|
||||||
|
infiniteRepeatable(
|
||||||
|
animation = tween(
|
||||||
|
durationMillis = RotationDuration,
|
||||||
|
easing = LinearEasing
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val endAngle = transition.animateFloat(
|
||||||
|
0f,
|
||||||
|
JumpRotationAngle,
|
||||||
|
infiniteRepeatable(
|
||||||
|
animation = keyframes {
|
||||||
|
durationMillis = HeadAndTailAnimationDuration + HeadAndTailDelayDuration
|
||||||
|
0f at 0 with CircularEasing
|
||||||
|
JumpRotationAngle at HeadAndTailAnimationDuration
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val startAngle = transition.animateFloat(
|
||||||
|
0f,
|
||||||
|
JumpRotationAngle,
|
||||||
|
infiniteRepeatable(
|
||||||
|
animation = keyframes {
|
||||||
|
durationMillis = HeadAndTailAnimationDuration + HeadAndTailDelayDuration
|
||||||
|
0f at HeadAndTailDelayDuration with CircularEasing
|
||||||
|
JumpRotationAngle at durationMillis
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Canvas(
|
||||||
|
modifier
|
||||||
|
.progressSemantics()
|
||||||
|
.size(CircularIndicatorDiameter)
|
||||||
|
) {
|
||||||
|
drawCircularIndicator(0f, 360f, Color.Transparent, stroke)
|
||||||
|
val currentRotationAngleOffset = (currentRotation.value * RotationAngleOffset) % 360f
|
||||||
|
|
||||||
|
val sweep = abs(endAngle.value - startAngle.value)
|
||||||
|
|
||||||
|
val offset = StartAngleOffset + currentRotationAngleOffset + baseRotation.value
|
||||||
|
drawIndeterminateCircularIndicator(
|
||||||
|
startAngle.value + offset,
|
||||||
|
strokeWidth,
|
||||||
|
sweep,
|
||||||
|
brush,
|
||||||
|
stroke
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DrawScope.drawCircularIndicator(
|
||||||
|
startAngle: Float,
|
||||||
|
sweep: Float,
|
||||||
|
color: Color,
|
||||||
|
stroke: Stroke
|
||||||
|
) {
|
||||||
|
val diameterOffset = stroke.width / 2
|
||||||
|
val arcDimen = size.width - 2 * diameterOffset
|
||||||
|
drawArc(
|
||||||
|
color = color,
|
||||||
|
startAngle = startAngle,
|
||||||
|
sweepAngle = sweep,
|
||||||
|
useCenter = false,
|
||||||
|
topLeft = Offset(diameterOffset, diameterOffset),
|
||||||
|
size = Size(arcDimen, arcDimen),
|
||||||
|
style = stroke
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DrawScope.drawCircularIndicator(
|
||||||
|
startAngle: Float,
|
||||||
|
sweep: Float,
|
||||||
|
brush: Brush,
|
||||||
|
stroke: Stroke
|
||||||
|
) {
|
||||||
|
val diameterOffset = stroke.width / 2
|
||||||
|
val arcDimen = size.width - 2 * diameterOffset
|
||||||
|
drawArc(
|
||||||
|
brush = brush,
|
||||||
|
startAngle = startAngle,
|
||||||
|
sweepAngle = sweep,
|
||||||
|
useCenter = false,
|
||||||
|
topLeft = Offset(diameterOffset, diameterOffset),
|
||||||
|
size = Size(arcDimen, arcDimen),
|
||||||
|
style = stroke
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DrawScope.drawIndeterminateCircularIndicator(
|
||||||
|
startAngle: Float,
|
||||||
|
strokeWidth: Dp,
|
||||||
|
sweep: Float,
|
||||||
|
brush: Brush,
|
||||||
|
stroke: Stroke
|
||||||
|
) {
|
||||||
|
val strokeCapOffset = if (stroke.cap == StrokeCap.Butt) {
|
||||||
|
0f
|
||||||
|
} else {
|
||||||
|
(180.0 / PI).toFloat() * (strokeWidth / (CircularIndicatorDiameter / 2)) / 2f
|
||||||
|
}
|
||||||
|
val adjustedStartAngle = startAngle + strokeCapOffset
|
||||||
|
val adjustedSweep = max(sweep, 0.1f)
|
||||||
|
drawCircularIndicator(adjustedStartAngle, adjustedSweep, brush, stroke)
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val RotationsPerCycle = 5
|
||||||
|
private const val RotationDuration = 1332
|
||||||
|
private const val StartAngleOffset = -90f
|
||||||
|
private const val BaseRotationAngle = 286f
|
||||||
|
private const val JumpRotationAngle = 290f
|
||||||
|
private val CircularIndicatorDiameter = 38.dp
|
||||||
|
private const val RotationAngleOffset = (BaseRotationAngle + JumpRotationAngle) % 360f
|
||||||
|
private const val HeadAndTailAnimationDuration = (RotationDuration * 0.5).toInt()
|
||||||
|
private const val HeadAndTailDelayDuration = HeadAndTailAnimationDuration
|
||||||
|
private val CircularEasing = CubicBezierEasing(0.4f, 0f, 0.2f, 1f)
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
private fun AllInLoadingPreview() {
|
||||||
|
AllInTheme {
|
||||||
|
AllInLoading()
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,30 @@
|
|||||||
package fr.iut.alldev.allin.ui.login
|
package fr.iut.alldev.allin.ui.login
|
||||||
|
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class LoginViewModel @Inject constructor(
|
class LoginViewModel @Inject constructor(
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
var loading = mutableStateOf(false)
|
||||||
|
fun onLogin(
|
||||||
|
navigateToDashboard: ()->Unit
|
||||||
|
){
|
||||||
|
viewModelScope.launch {
|
||||||
|
loading.value = true
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
Thread.sleep(3000)
|
||||||
|
}
|
||||||
|
navigateToDashboard()
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in new issue