Merge pull request #826 from vector-im/feature/bma/swipeAction
Improve swipe to reply rendering
This commit is contained in:
commit
4afdccb3ed
18 changed files with 205 additions and 93 deletions
1
.idea/dictionaries/shared.xml
generated
1
.idea/dictionaries/shared.xml
generated
|
|
@ -9,6 +9,7 @@
|
||||||
<w>placeables</w>
|
<w>placeables</w>
|
||||||
<w>showkase</w>
|
<w>showkase</w>
|
||||||
<w>snackbar</w>
|
<w>snackbar</w>
|
||||||
|
<w>swipeable</w>
|
||||||
<w>textfields</w>
|
<w>textfields</w>
|
||||||
</words>
|
</words>
|
||||||
</dictionary>
|
</dictionary>
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@ koverMerged {
|
||||||
excludes += "io.element.android.features.messages.impl.media.local.LocalMediaViewState"
|
excludes += "io.element.android.features.messages.impl.media.local.LocalMediaViewState"
|
||||||
excludes += "io.element.android.features.location.impl.map.MapState"
|
excludes += "io.element.android.features.location.impl.map.MapState"
|
||||||
excludes += "io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState*"
|
excludes += "io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState*"
|
||||||
|
excludes += "io.element.android.libraries.designsystem.swipe.SwipeableActionsState*"
|
||||||
}
|
}
|
||||||
bound {
|
bound {
|
||||||
minValue = 90
|
minValue = 90
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,13 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@file:OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.timeline.components
|
package io.element.android.features.messages.impl.timeline.components
|
||||||
|
|
||||||
import androidx.compose.foundation.Canvas
|
import androidx.compose.foundation.Canvas
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.gestures.Orientation
|
||||||
|
import androidx.compose.foundation.gestures.draggable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
|
@ -28,6 +28,7 @@ import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.absoluteOffset
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
|
@ -35,26 +36,25 @@ import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.DismissDirection
|
|
||||||
import androidx.compose.material3.DismissState
|
|
||||||
import androidx.compose.material3.DismissValue
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.SwipeToDismiss
|
|
||||||
import androidx.compose.material3.rememberDismissState
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.draw.clipToBounds
|
import androidx.compose.ui.draw.clipToBounds
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.platform.LocalViewConfiguration
|
||||||
|
import androidx.compose.ui.platform.ViewConfiguration
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.IntOffset
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.compose.ui.zIndex
|
import androidx.compose.ui.zIndex
|
||||||
|
|
@ -66,6 +66,7 @@ import io.element.android.features.messages.impl.timeline.aTimelineItemReactions
|
||||||
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
|
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
|
||||||
import io.element.android.features.messages.impl.timeline.components.event.toExtraPadding
|
import io.element.android.features.messages.impl.timeline.components.event.toExtraPadding
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
|
import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition
|
||||||
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState
|
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
|
||||||
|
|
@ -78,6 +79,9 @@ import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||||
|
import io.element.android.libraries.designsystem.swipe.SwipeableActionsState
|
||||||
|
import io.element.android.libraries.designsystem.swipe.rememberSwipeableActionsState
|
||||||
|
import io.element.android.libraries.designsystem.text.toPx
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
|
@ -93,7 +97,10 @@ import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
|
||||||
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
|
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
|
||||||
import io.element.android.libraries.theme.ElementTheme
|
import io.element.android.libraries.theme.ElementTheme
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TimelineItemEventRow(
|
fun TimelineItemEventRow(
|
||||||
|
|
@ -110,6 +117,7 @@ fun TimelineItemEventRow(
|
||||||
onSwipeToReply: () -> Unit,
|
onSwipeToReply: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
|
||||||
fun onUserDataClicked() {
|
fun onUserDataClicked() {
|
||||||
|
|
@ -121,56 +129,88 @@ fun TimelineItemEventRow(
|
||||||
inReplyToClick(inReplyToEventId)
|
inReplyToClick(inReplyToEventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canReply) {
|
Column(modifier = modifier.fillMaxWidth()) {
|
||||||
val dismissState = rememberDismissState(
|
if (event.groupPosition.isNew()) {
|
||||||
confirmValueChange = {
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
if (it == DismissValue.DismissedToEnd) {
|
} else {
|
||||||
onSwipeToReply()
|
Spacer(modifier = Modifier.height(2.dp))
|
||||||
|
}
|
||||||
|
if (canReply) {
|
||||||
|
val state: SwipeableActionsState = rememberSwipeableActionsState()
|
||||||
|
val offset = state.offset.value
|
||||||
|
val swipeThresholdPx = 40.dp.toPx()
|
||||||
|
val thresholdCrossed = abs(offset) > swipeThresholdPx
|
||||||
|
SwipeSensitivity(3f) {
|
||||||
|
Box(Modifier.fillMaxWidth()) {
|
||||||
|
Row(modifier = Modifier.matchParentSize()) {
|
||||||
|
ReplySwipeIndicator({ offset / 120 })
|
||||||
|
}
|
||||||
|
TimelineItemEventRowContent(
|
||||||
|
modifier = Modifier
|
||||||
|
.absoluteOffset { IntOffset(x = offset.roundToInt(), y = 0) }
|
||||||
|
.draggable(
|
||||||
|
orientation = Orientation.Horizontal,
|
||||||
|
enabled = !state.isResettingOnRelease,
|
||||||
|
onDragStopped = {
|
||||||
|
coroutineScope.launch {
|
||||||
|
if (thresholdCrossed) {
|
||||||
|
onSwipeToReply()
|
||||||
|
}
|
||||||
|
state.resetOffset()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state = state.draggableState,
|
||||||
|
),
|
||||||
|
event = event,
|
||||||
|
isHighlighted = isHighlighted,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
onClick = onClick,
|
||||||
|
onLongClick = onLongClick,
|
||||||
|
onTimestampClicked = onTimestampClicked,
|
||||||
|
inReplyToClicked = ::inReplyToClicked,
|
||||||
|
onUserDataClicked = ::onUserDataClicked,
|
||||||
|
onReactionClicked = { emoji -> onReactionClick(emoji, event) },
|
||||||
|
onMoreReactionsClicked = { onMoreReactionsClick(event) },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
// Do not dismiss the message, return false!
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
)
|
} else {
|
||||||
SwipeToDismiss(
|
TimelineItemEventRowContent(
|
||||||
state = dismissState,
|
event = event,
|
||||||
background = {
|
isHighlighted = isHighlighted,
|
||||||
ReplySwipeIndicator({ dismissState.toSwipeProgress() })
|
interactionSource = interactionSource,
|
||||||
},
|
onClick = onClick,
|
||||||
directions = setOf(DismissDirection.StartToEnd),
|
onLongClick = onLongClick,
|
||||||
dismissContent = {
|
onTimestampClicked = onTimestampClicked,
|
||||||
TimelineItemEventRowContent(
|
inReplyToClicked = ::inReplyToClicked,
|
||||||
event = event,
|
onUserDataClicked = ::onUserDataClicked,
|
||||||
isHighlighted = isHighlighted,
|
onReactionClicked = { emoji -> onReactionClick(emoji, event) },
|
||||||
interactionSource = interactionSource,
|
onMoreReactionsClicked = { onMoreReactionsClick(event) },
|
||||||
onClick = onClick,
|
)
|
||||||
onLongClick = onLongClick,
|
}
|
||||||
onTimestampClicked = onTimestampClicked,
|
|
||||||
inReplyToClicked = ::inReplyToClicked,
|
|
||||||
onUserDataClicked = ::onUserDataClicked,
|
|
||||||
onReactionClicked = { emoji -> onReactionClick(emoji, event) },
|
|
||||||
onMoreReactionsClicked = { onMoreReactionsClick(event) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
TimelineItemEventRowContent(
|
|
||||||
event = event,
|
|
||||||
isHighlighted = isHighlighted,
|
|
||||||
interactionSource = interactionSource,
|
|
||||||
onClick = onClick,
|
|
||||||
onLongClick = onLongClick,
|
|
||||||
onTimestampClicked = onTimestampClicked,
|
|
||||||
inReplyToClicked = ::inReplyToClicked,
|
|
||||||
onUserDataClicked = ::onUserDataClicked,
|
|
||||||
onReactionClicked = { emoji -> onReactionClick(emoji, event) },
|
|
||||||
onMoreReactionsClicked = { onMoreReactionsClick(event) },
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
// This is assuming that we are in a ColumnScope, but this is OK, for both Preview and real usage.
|
}
|
||||||
if (event.groupPosition.isNew()) {
|
|
||||||
Spacer(modifier = modifier.height(16.dp))
|
/**
|
||||||
} else {
|
* Impact ViewConfiguration.touchSlop by [sensitivityFactor].
|
||||||
Spacer(modifier = modifier.height(2.dp))
|
* Inspired from https://issuetracker.google.com/u/1/issues/269627294.
|
||||||
|
* @param sensitivityFactor the factor to multiply the touchSlop by. The highest value, the more the user will
|
||||||
|
* have to drag to start the drag.
|
||||||
|
* @param content the content to display.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun SwipeSensitivity(
|
||||||
|
sensitivityFactor: Float,
|
||||||
|
content: @Composable () -> Unit,
|
||||||
|
) {
|
||||||
|
val current = LocalViewConfiguration.current
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalViewConfiguration provides object : ViewConfiguration by current {
|
||||||
|
override val touchSlop: Float
|
||||||
|
get() = current.touchSlop * sensitivityFactor
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
content()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -266,14 +306,6 @@ private fun TimelineItemEventRowContent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun DismissState.toSwipeProgress(): Float {
|
|
||||||
return when (targetValue) {
|
|
||||||
DismissValue.Default -> 0f
|
|
||||||
DismissValue.DismissedToEnd -> progress * 3
|
|
||||||
DismissValue.DismissedToStart -> progress * 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MessageSenderInformation(
|
private fun MessageSenderInformation(
|
||||||
sender: String,
|
sender: String,
|
||||||
|
|
@ -544,6 +576,7 @@ private fun ContentToPreview() {
|
||||||
body = "A long text which will be displayed on several lines and" +
|
body = "A long text which will be displayed on several lines and" +
|
||||||
" hopefully can be manually adjusted to test different behaviors."
|
" hopefully can be manually adjusted to test different behaviors."
|
||||||
),
|
),
|
||||||
|
groupPosition = TimelineItemGroupPosition.First,
|
||||||
),
|
),
|
||||||
isHighlighted = false,
|
isHighlighted = false,
|
||||||
canReply = true,
|
canReply = true,
|
||||||
|
|
@ -562,6 +595,7 @@ private fun ContentToPreview() {
|
||||||
content = aTimelineItemImageContent().copy(
|
content = aTimelineItemImageContent().copy(
|
||||||
aspectRatio = 5f
|
aspectRatio = 5f
|
||||||
),
|
),
|
||||||
|
groupPosition = TimelineItemGroupPosition.Last,
|
||||||
),
|
),
|
||||||
isHighlighted = false,
|
isHighlighted = false,
|
||||||
canReply = true,
|
canReply = true,
|
||||||
|
|
@ -606,7 +640,8 @@ private fun ContentToPreviewWithReply() {
|
||||||
body = "A long text which will be displayed on several lines and" +
|
body = "A long text which will be displayed on several lines and" +
|
||||||
" hopefully can be manually adjusted to test different behaviors."
|
" hopefully can be manually adjusted to test different behaviors."
|
||||||
),
|
),
|
||||||
inReplyTo = aInReplyToReady(replyContent)
|
inReplyTo = aInReplyToReady(replyContent),
|
||||||
|
groupPosition = TimelineItemGroupPosition.First,
|
||||||
),
|
),
|
||||||
isHighlighted = false,
|
isHighlighted = false,
|
||||||
canReply = true,
|
canReply = true,
|
||||||
|
|
@ -625,7 +660,8 @@ private fun ContentToPreviewWithReply() {
|
||||||
content = aTimelineItemImageContent().copy(
|
content = aTimelineItemImageContent().copy(
|
||||||
aspectRatio = 5f
|
aspectRatio = 5f
|
||||||
),
|
),
|
||||||
inReplyTo = aInReplyToReady(replyContent)
|
inReplyTo = aInReplyToReady(replyContent),
|
||||||
|
groupPosition = TimelineItemGroupPosition.Last,
|
||||||
),
|
),
|
||||||
isHighlighted = false,
|
isHighlighted = false,
|
||||||
canReply = true,
|
canReply = true,
|
||||||
|
|
@ -699,7 +735,6 @@ private fun ContentTimestampToPreview(event: TimelineItem.Event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
internal fun TimelineItemEventRowWithManyReactionsLightPreview() =
|
internal fun TimelineItemEventRowWithManyReactionsLightPreview() =
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.designsystem.swipe
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.Animatable
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.foundation.MutatePriority
|
||||||
|
import androidx.compose.foundation.gestures.DraggableState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inspired from https://github.com/bmarty/swipe/blob/trunk/swipe/src/main/kotlin/me/saket/swipe/SwipeableActionsState.kt
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun rememberSwipeableActionsState(): SwipeableActionsState {
|
||||||
|
return remember { SwipeableActionsState() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
class SwipeableActionsState {
|
||||||
|
/**
|
||||||
|
* The current position (in pixels) of the content.
|
||||||
|
*/
|
||||||
|
val offset: State<Float> get() = offsetState
|
||||||
|
private var offsetState = mutableStateOf(0f)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the content is currently animating to reset its offset after it was swiped.
|
||||||
|
*/
|
||||||
|
var isResettingOnRelease: Boolean by mutableStateOf(false)
|
||||||
|
private set
|
||||||
|
|
||||||
|
val draggableState = DraggableState { delta ->
|
||||||
|
val targetOffset = offsetState.value + delta
|
||||||
|
val isAllowed = isResettingOnRelease || targetOffset > 0f
|
||||||
|
|
||||||
|
offsetState.value += if (isAllowed) delta else 0f
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun resetOffset() {
|
||||||
|
draggableState.drag(MutatePriority.PreventUserInput) {
|
||||||
|
isResettingOnRelease = true
|
||||||
|
try {
|
||||||
|
Animatable(offsetState.value).animateTo(
|
||||||
|
targetValue = 0f,
|
||||||
|
animationSpec = tween(durationMillis = 300),
|
||||||
|
) {
|
||||||
|
dragBy(value - offsetState.value)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
isResettingOnRelease = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:b8ca3e1c8ce3c9476cdc7db46aebc8b6970486ff01e05b82c162256373c8957f
|
oid sha256:323b80f3b2fcee42ffe50f333ff9d3952da05138f413f15882bf8ff7079347d8
|
||||||
size 141055
|
size 138598
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:2bf89d0228a9771360eaad8a0bc514731153a351551df427cf7f7e44b886edfe
|
oid sha256:21c3a584e3d47d030e45aac8d2162bd9b1d03f0ef43c3b8681f69eadd2d6fcc2
|
||||||
size 145398
|
size 142344
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:043097099dd08f0e192debd0ef650e8effe76c71f29dd3a993bf756d8e933d25
|
oid sha256:7d62f35da442eba33e6913f6704de1e80918e50cef6237c00678ec9de5a23759
|
||||||
size 62314
|
size 62310
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:4fe9d9d0ff870ccbb095c53b37900c00feb8153e76965ba432bad9d37387e512
|
oid sha256:8d488277351ea1f53f11cabef46ff46768a42d61dc913b903b670635b9e44816
|
||||||
size 64523
|
size 64503
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:e78c43cdaa5532d854087567fb8deef13fe89b1e896450428186df6b6cd83443
|
oid sha256:e6b3750046ba185168395f20c75451524754b003a293cfcb0ed5a45be8f3335b
|
||||||
size 68731
|
size 68707
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:960e7c05075107db3a2b57f630f114927d4d019ffc5d38f5d501d44107753ea5
|
oid sha256:963bf003585c82dddbb2efe1f6cf2f0564db88ea5de69e2b4322a6d256639de8
|
||||||
size 70789
|
size 70761
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f39fd269e68edb95e0b7c49bbf21a51c6c4233bfbfc173fdbc0e80eec2cf8d83
|
oid sha256:b75482695c36693390a1100ff2758ed8910f65da42ddb270cbc07252c25ae439
|
||||||
size 64252
|
size 64252
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:eeb7f709da25c8864e712ab1532fd7803ab1d692c7baea970283a4702640840c
|
oid sha256:401cbd6022543957388ec5fe81181604495238e80c95c1baa036c67bd62460d6
|
||||||
size 66900
|
size 66869
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:79493cad80934a053f300864189dd47bf6c1d9aabac780a5c5ea1ca497fc7a47
|
oid sha256:ac20d25de28ae59dd2e9ea2743480792c3c10644128219966af1eee1e2034ac0
|
||||||
size 71250
|
size 71215
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:c2b4ed0df17a6f96dd0031b820585346ec6ccac419a2f1237e0decdfb50dddb7
|
oid sha256:5ca3befc8d4142f5be8252ffffef5ac82a25786085b0552b348d4f0a412bc9ae
|
||||||
size 73644
|
size 73743
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:82b4fb72cd3517b4eedeba3172cc428ba67199df816c686d998027ed021c1bb0
|
oid sha256:1963827d549b8144bd1c71f85da6208307b8939f621b57c705a2ca61309f2e0d
|
||||||
size 84164
|
size 84239
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:7c95c7df9616dca071528f4185e63485e7e738e51e595d828f94b08205c6e33d
|
oid sha256:6e4ae356384002a19f779236ed6f2a3722c8f7bda91357dee3cacbb95d6aa61e
|
||||||
size 87695
|
size 87794
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:02f2eb6e37f1022aa13cbcdfc34e7955b99ee4c3d6b35668b548afc94d33ae31
|
oid sha256:dcd2fb7256950a8a31267d6be90c342ae8774e945b04d954a7cc3d83878cb25a
|
||||||
size 119137
|
size 119550
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d50e5ac4433e4a44091623f1813373884007506002cc0c657ca7231a6e309462
|
oid sha256:5628adde34216d1888481918628c82655252acfc15296c4fddd3220d0b72eaa4
|
||||||
size 123938
|
size 124893
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue