Merge pull request #5981 from element-hq/feature/bma/iterateOnVerificationScreen

Iterate on verification screen
This commit is contained in:
Benoit Marty 2026-01-09 10:24:11 +01:00 committed by GitHub
commit 74b835f734
36 changed files with 184 additions and 249 deletions

View file

@ -21,10 +21,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.focused import androidx.compose.ui.semantics.focused
import androidx.compose.ui.semantics.progressBarRangeInfo
import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
@ -45,7 +43,6 @@ import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.InvisibleButton
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.components.TopAppBar
@ -73,11 +70,7 @@ fun IncomingVerificationView(
TopAppBar( TopAppBar(
title = {}, title = {},
navigationIcon = { navigationIcon = {
when { BackButton(onClick = { state.eventSink(IncomingVerificationViewEvents.GoBack) })
step is Step.Initial && !step.isWaiting -> Unit
step is Step.Completed -> Unit
else -> BackButton(onClick = { state.eventSink(IncomingVerificationViewEvents.GoBack) })
}
}, },
colors = topAppBarColors(containerColor = Color.Transparent), colors = topAppBarColors(containerColor = Color.Transparent),
) )
@ -103,19 +96,11 @@ fun IncomingVerificationView(
private fun IncomingVerificationHeader(step: Step, request: VerificationRequest.Incoming) { private fun IncomingVerificationHeader(step: Step, request: VerificationRequest.Incoming) {
val iconStyle = when (step) { val iconStyle = when (step) {
Step.Canceled -> BigIcon.Style.AlertSolid Step.Canceled -> BigIcon.Style.AlertSolid
is Step.Initial -> if (step.isWaiting) { is Step.Initial -> when (request) {
BigIcon.Style.Loading is VerificationRequest.Incoming.OtherSession -> BigIcon.Style.Default(CompoundIcons.Devices())
} else { is VerificationRequest.Incoming.User -> BigIcon.Style.Default(CompoundIcons.UserProfileSolid())
when (request) {
is VerificationRequest.Incoming.OtherSession -> BigIcon.Style.Default(CompoundIcons.LockSolid())
is VerificationRequest.Incoming.User -> BigIcon.Style.Default(CompoundIcons.UserProfileSolid())
}
}
is Step.Verifying -> if (step.isWaiting) {
BigIcon.Style.Loading
} else {
BigIcon.Style.Default(CompoundIcons.ReactionSolid())
} }
is Step.Verifying -> BigIcon.Style.Default(CompoundIcons.ReactionSolid())
Step.Completed -> BigIcon.Style.SuccessSolid Step.Completed -> BigIcon.Style.SuccessSolid
Step.Failure -> BigIcon.Style.AlertSolid Step.Failure -> BigIcon.Style.AlertSolid
} }
@ -159,10 +144,6 @@ private fun IncomingVerificationHeader(step: Step, request: VerificationRequest.
.semantics(mergeDescendants = true) { .semantics(mergeDescendants = true) {
contentDescription = timeLimitMessage contentDescription = timeLimitMessage
focused = true focused = true
if (iconStyle == BigIcon.Style.Loading) {
// Same code than Modifier.progressSemantics()
progressBarRangeInfo = ProgressBarRangeInfo.Indeterminate
}
} }
.focusable(), .focusable(),
iconStyle = iconStyle, iconStyle = iconStyle,
@ -185,7 +166,7 @@ private fun IncomingVerificationContent(
@Composable @Composable
private fun ContentInitial( private fun ContentInitial(
initialIncoming: Step.Initial, stepInitial: Step.Initial,
request: VerificationRequest.Incoming, request: VerificationRequest.Incoming,
) { ) {
when (request) { when (request) {
@ -195,9 +176,9 @@ private fun ContentInitial(
verticalArrangement = Arrangement.spacedBy(24.dp), verticalArrangement = Arrangement.spacedBy(24.dp),
) { ) {
SessionDetailsView( SessionDetailsView(
deviceName = initialIncoming.deviceDisplayName, deviceName = stepInitial.deviceDisplayName,
deviceId = initialIncoming.deviceId, deviceId = stepInitial.deviceId,
signInFormattedTimestamp = initialIncoming.formattedSignInTime, signInFormattedTimestamp = stepInitial.formattedSignInTime,
) )
Text( Text(
modifier = Modifier modifier = Modifier
@ -227,48 +208,42 @@ private fun ContentInitial(
private fun IncomingVerificationBottomMenu( private fun IncomingVerificationBottomMenu(
state: IncomingVerificationState, state: IncomingVerificationState,
) { ) {
val step = state.step
val eventSink = state.eventSink val eventSink = state.eventSink
when (val step = state.step) {
when (step) {
is Step.Initial -> { is Step.Initial -> {
if (step.isWaiting) { VerificationBottomMenu {
// Show nothing Button(
} else { modifier = Modifier.fillMaxWidth(),
VerificationBottomMenu { text = stringResource(CommonStrings.action_start_verification),
Button( enabled = !step.isWaiting,
modifier = Modifier.fillMaxWidth(), showProgress = step.isWaiting,
text = stringResource(CommonStrings.action_start_verification), onClick = { eventSink(IncomingVerificationViewEvents.StartVerification) },
onClick = { eventSink(IncomingVerificationViewEvents.StartVerification) }, )
) TextButton(
TextButton( modifier = Modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth(), text = stringResource(CommonStrings.action_ignore),
text = stringResource(CommonStrings.action_ignore), enabled = !step.isWaiting,
onClick = { eventSink(IncomingVerificationViewEvents.IgnoreVerification) }, onClick = { eventSink(IncomingVerificationViewEvents.IgnoreVerification) },
) )
}
} }
} }
is Step.Verifying -> { is Step.Verifying -> {
if (step.isWaiting) { VerificationBottomMenu {
// Add invisible buttons to keep the same screen layout Button(
VerificationBottomMenu { modifier = Modifier.fillMaxWidth(),
InvisibleButton() text = stringResource(R.string.screen_session_verification_they_match),
InvisibleButton() enabled = !step.isWaiting,
} showProgress = step.isWaiting,
} else { onClick = {
VerificationBottomMenu { eventSink(IncomingVerificationViewEvents.ConfirmVerification)
Button( },
modifier = Modifier.fillMaxWidth(), )
text = stringResource(R.string.screen_session_verification_they_match), TextButton(
onClick = { eventSink(IncomingVerificationViewEvents.ConfirmVerification) }, modifier = Modifier.fillMaxWidth(),
) text = stringResource(R.string.screen_session_verification_they_dont_match),
TextButton( enabled = !step.isWaiting,
modifier = Modifier.fillMaxWidth(), onClick = { eventSink(IncomingVerificationViewEvents.DeclineVerification) },
text = stringResource(R.string.screen_session_verification_they_dont_match), )
onClick = { eventSink(IncomingVerificationViewEvents.DeclineVerification) },
)
}
} }
} }
Step.Canceled, Step.Canceled,
@ -278,7 +253,9 @@ private fun IncomingVerificationBottomMenu(
Button( Button(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
text = stringResource(CommonStrings.action_done), text = stringResource(CommonStrings.action_done),
onClick = { eventSink(IncomingVerificationViewEvents.GoBack) }, onClick = {
eventSink(IncomingVerificationViewEvents.GoBack)
},
) )
} }
} }

View file

@ -24,10 +24,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.focused import androidx.compose.ui.semantics.focused
import androidx.compose.ui.semantics.progressBarRangeInfo
import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.semantics
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
@ -37,7 +35,6 @@ import io.element.android.features.verifysession.impl.R
import io.element.android.features.verifysession.impl.outgoing.OutgoingVerificationState.Step import io.element.android.features.verifysession.impl.outgoing.OutgoingVerificationState.Step
import io.element.android.features.verifysession.impl.ui.VerificationBottomMenu import io.element.android.features.verifysession.impl.ui.VerificationBottomMenu
import io.element.android.features.verifysession.impl.ui.VerificationContentVerifying import io.element.android.features.verifysession.impl.ui.VerificationContentVerifying
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.components.BigIcon
@ -96,20 +93,18 @@ fun OutgoingVerificationView(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = {}, title = {},
navigationIcon = if (step != Step.Completed) { navigationIcon = {
{ BackButton(onClick = ::cancelOrResetFlow) } BackButton(onClick = ::cancelOrResetFlow)
} else {
{}
}, },
colors = topAppBarColors(containerColor = Color.Transparent) colors = topAppBarColors(containerColor = Color.Transparent),
) )
}, },
header = { header = {
OutgoingVerificationHeader(step = step, request = state.request) OutgoingVerificationHeader(step = step, request = state.request)
}, },
footer = { footer = {
OutgoingVerificationViewBottomMenu( OutgoingVerificationBottomMenu(
screenState = state, state = state,
onCancelClick = ::cancelOrResetFlow, onCancelClick = ::cancelOrResetFlow,
onContinueClick = onFinish, onContinueClick = onFinish,
) )
@ -117,7 +112,7 @@ fun OutgoingVerificationView(
isScrollable = true, isScrollable = true,
) { ) {
OutgoingVerificationContent( OutgoingVerificationContent(
flowState = step, step = step,
request = state.request, request = state.request,
onLearnMoreClick = onLearnMoreClick, onLearnMoreClick = onLearnMoreClick,
) )
@ -129,20 +124,16 @@ fun OutgoingVerificationView(
private fun OutgoingVerificationHeader(step: Step, request: VerificationRequest.Outgoing) { private fun OutgoingVerificationHeader(step: Step, request: VerificationRequest.Outgoing) {
val iconStyle = when (step) { val iconStyle = when (step) {
Step.Loading -> error("Should not happen") Step.Loading -> error("Should not happen")
Step.AwaitingOtherDeviceResponse,
Step.Initial -> when (request) { Step.Initial -> when (request) {
is VerificationRequest.Outgoing.CurrentSession -> BigIcon.Style.Default(CompoundIcons.Devices()) is VerificationRequest.Outgoing.CurrentSession -> BigIcon.Style.Default(CompoundIcons.Devices())
is VerificationRequest.Outgoing.User -> BigIcon.Style.Default(CompoundIcons.LockSolid()) is VerificationRequest.Outgoing.User -> BigIcon.Style.Default(CompoundIcons.UserProfileSolid())
} }
Step.AwaitingOtherDeviceResponse -> BigIcon.Style.Loading
Step.Canceled -> BigIcon.Style.AlertSolid Step.Canceled -> BigIcon.Style.AlertSolid
Step.Ready -> BigIcon.Style.Default(CompoundIcons.ReactionSolid()) Step.Ready -> BigIcon.Style.Default(CompoundIcons.ReactionSolid())
Step.Completed -> BigIcon.Style.SuccessSolid Step.Completed -> BigIcon.Style.SuccessSolid
is Step.Verifying -> { is Step.Verifying -> {
if (step.state is AsyncData.Loading<Unit>) { BigIcon.Style.Default(CompoundIcons.ReactionSolid())
BigIcon.Style.Loading
} else {
BigIcon.Style.Default(CompoundIcons.ReactionSolid())
}
} }
is Step.Exit -> return is Step.Exit -> return
} }
@ -201,10 +192,6 @@ private fun OutgoingVerificationHeader(step: Step, request: VerificationRequest.
.semantics(mergeDescendants = true) { .semantics(mergeDescendants = true) {
contentDescription = timeLimitMessage contentDescription = timeLimitMessage
focused = true focused = true
if (iconStyle == BigIcon.Style.Loading) {
// Same code than Modifier.progressSemantics()
progressBarRangeInfo = ProgressBarRangeInfo.Indeterminate
}
} }
.focusable(), .focusable(),
iconStyle = iconStyle, iconStyle = iconStyle,
@ -215,20 +202,16 @@ private fun OutgoingVerificationHeader(step: Step, request: VerificationRequest.
@Composable @Composable
private fun OutgoingVerificationContent( private fun OutgoingVerificationContent(
flowState: Step, step: Step,
request: VerificationRequest.Outgoing, request: VerificationRequest.Outgoing,
onLearnMoreClick: () -> Unit, onLearnMoreClick: () -> Unit,
) { ) {
when (flowState) { when (step) {
is Step.Initial -> { is Step.Initial -> when (request) {
when (request) { is VerificationRequest.Outgoing.CurrentSession -> Unit
is VerificationRequest.Outgoing.CurrentSession -> Unit is VerificationRequest.Outgoing.User -> ContentInitial(onLearnMoreClick)
is VerificationRequest.Outgoing.User -> ContentInitial(onLearnMoreClick)
}
}
is Step.Verifying -> {
VerificationContentVerifying(flowState.data)
} }
is Step.Verifying -> VerificationContentVerifying(step.data)
else -> Unit else -> Unit
} }
} }
@ -252,23 +235,23 @@ private fun ContentInitial(
} }
@Composable @Composable
private fun OutgoingVerificationViewBottomMenu( private fun OutgoingVerificationBottomMenu(
screenState: OutgoingVerificationState, state: OutgoingVerificationState,
onCancelClick: () -> Unit, onCancelClick: () -> Unit,
onContinueClick: () -> Unit, onContinueClick: () -> Unit,
) { ) {
val verificationViewState = screenState.step val eventSink = state.eventSink
val eventSink = screenState.eventSink when (val step = state.step) {
val isVerifying = (verificationViewState as? Step.Verifying)?.state is AsyncData.Loading<Unit>
when (verificationViewState) {
Step.Loading -> error("Should not happen") Step.Loading -> error("Should not happen")
is Step.AwaitingOtherDeviceResponse,
is Step.Initial -> { is Step.Initial -> {
VerificationBottomMenu { VerificationBottomMenu {
val isWaiting = step is Step.AwaitingOtherDeviceResponse
Button( Button(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
text = stringResource(CommonStrings.action_start_verification), text = stringResource(CommonStrings.action_start_verification),
enabled = !isWaiting,
showProgress = isWaiting,
onClick = { eventSink(OutgoingVerificationViewEvents.RequestVerification) }, onClick = { eventSink(OutgoingVerificationViewEvents.RequestVerification) },
) )
InvisibleButton() InvisibleButton()
@ -298,30 +281,26 @@ private fun OutgoingVerificationViewBottomMenu(
) )
} }
} }
is Step.AwaitingOtherDeviceResponse -> Unit
is Step.Verifying -> { is Step.Verifying -> {
if (isVerifying) { val isVerifying = step.state.isLoading()
// Add invisible buttons to keep the same screen layout VerificationBottomMenu {
VerificationBottomMenu { Button(
InvisibleButton() modifier = Modifier.fillMaxWidth(),
InvisibleButton() text = stringResource(R.string.screen_session_verification_they_match),
} enabled = !isVerifying,
} else { showProgress = isVerifying,
VerificationBottomMenu { onClick = {
Button( eventSink(OutgoingVerificationViewEvents.ConfirmVerification)
modifier = Modifier.fillMaxWidth(), },
text = stringResource(R.string.screen_session_verification_they_match), )
onClick = { TextButton(
eventSink(OutgoingVerificationViewEvents.ConfirmVerification) modifier = Modifier.fillMaxWidth(),
}, text = stringResource(R.string.screen_session_verification_they_dont_match),
) enabled = !isVerifying,
onClick = {
TextButton( eventSink(OutgoingVerificationViewEvents.DeclineVerification)
modifier = Modifier.fillMaxWidth(), },
text = stringResource(R.string.screen_session_verification_they_dont_match), )
onClick = { eventSink(OutgoingVerificationViewEvents.DeclineVerification) },
)
}
} }
} }
is Step.Completed -> { is Step.Completed -> {
@ -334,7 +313,7 @@ private fun OutgoingVerificationViewBottomMenu(
InvisibleButton() InvisibleButton()
} }
} }
is Step.Exit -> return is Step.Exit -> Unit
} }
} }

View file

@ -34,7 +34,6 @@ import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
@ -82,11 +81,6 @@ object BigIcon {
* A success style with a tinted background. * A success style with a tinted background.
*/ */
data object SuccessSolid : Style data object SuccessSolid : Style
/**
* A loading style with the default background color.
*/
data object Loading : Style
} }
/** /**
@ -110,7 +104,6 @@ object BigIcon {
Style.Success -> Color.Transparent Style.Success -> Color.Transparent
Style.AlertSolid -> ElementTheme.colors.bgCriticalSubtle Style.AlertSolid -> ElementTheme.colors.bgCriticalSubtle
Style.SuccessSolid -> ElementTheme.colors.bgSuccessSubtle Style.SuccessSolid -> ElementTheme.colors.bgSuccessSubtle
Style.Loading -> ElementTheme.colors.bgSubtleSecondary
} }
Box( Box(
modifier = modifier modifier = modifier
@ -119,52 +112,39 @@ object BigIcon {
.background(backgroundColor), .background(backgroundColor),
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
) { ) {
if (style is Style.Loading) { val icon = when (style) {
CircularProgressIndicator( is Style.Default -> style.vectorIcon
modifier = Modifier.size(27.dp), Style.Alert,
color = ElementTheme.colors.iconSecondary, Style.AlertSolid -> CompoundIcons.ErrorSolid()
trackColor = Color.Transparent, Style.Success,
strokeWidth = 3.dp, Style.SuccessSolid -> CompoundIcons.CheckCircleSolid()
)
} else {
val icon = when (style) {
is Style.Default -> style.vectorIcon
Style.Alert,
Style.AlertSolid -> CompoundIcons.ErrorSolid()
Style.Success,
Style.SuccessSolid -> CompoundIcons.CheckCircleSolid()
Style.Loading -> error("This should never be reached")
}
val contentDescription = when (style) {
is Style.Default -> style.contentDescription
Style.Alert,
Style.AlertSolid -> stringResource(CommonStrings.common_error)
Style.Success,
Style.SuccessSolid -> stringResource(CommonStrings.common_success)
Style.Loading -> error("This should never be reached")
}
val iconTint = when (style) {
is Style.Default -> if (style.useCriticalTint) {
ElementTheme.colors.iconCriticalPrimary
} else if (style.usePrimaryTint) {
ElementTheme.colors.iconPrimary
} else {
ElementTheme.colors.iconSecondary
}
Style.Alert,
Style.AlertSolid -> ElementTheme.colors.iconCriticalPrimary
Style.Success,
Style.SuccessSolid -> ElementTheme.colors.iconSuccessPrimary
Style.Loading -> error("This should never be reached")
}
Icon(
modifier = Modifier.size(32.dp),
tint = iconTint,
imageVector = icon,
contentDescription = contentDescription
)
} }
val contentDescription = when (style) {
is Style.Default -> style.contentDescription
Style.Alert,
Style.AlertSolid -> stringResource(CommonStrings.common_error)
Style.Success,
Style.SuccessSolid -> stringResource(CommonStrings.common_success)
}
val iconTint = when (style) {
is Style.Default -> if (style.useCriticalTint) {
ElementTheme.colors.iconCriticalPrimary
} else if (style.usePrimaryTint) {
ElementTheme.colors.iconPrimary
} else {
ElementTheme.colors.iconSecondary
}
Style.Alert,
Style.AlertSolid -> ElementTheme.colors.iconCriticalPrimary
Style.Success,
Style.SuccessSolid -> ElementTheme.colors.iconSuccessPrimary
}
Icon(
modifier = Modifier.size(32.dp),
tint = iconTint,
imageVector = icon,
contentDescription = contentDescription
)
} }
} }
} }
@ -199,6 +179,5 @@ internal class BigIconStyleProvider : PreviewParameterProvider<BigIcon.Style> {
BigIcon.Style.Default(Icons.Filled.CatchingPokemon, useCriticalTint = true), BigIcon.Style.Default(Icons.Filled.CatchingPokemon, useCriticalTint = true),
BigIcon.Style.Success, BigIcon.Style.Success,
BigIcon.Style.SuccessSolid, BigIcon.Style.SuccessSolid,
BigIcon.Style.Loading,
) )
} }

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:fc82b350fa0905ffa7fdb86042157aa9577e4b60dd04cd0c875d6c26f8f18a05 oid sha256:b8edbc22543c204a226c3acc78dbe346c90a5806f374b97fce320777e6a4d97f
size 74382 size 77061

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:2c51ecde00a75a74fb6b654f58f8c6507fe1af50f5f996c27bafa355d01f3f58 oid sha256:1535ae46ff8d99e1205966bee0c7a0455505f4ab4499805350f7aeff4931a3e8
size 42097 size 42380

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:11df4acac503d00011ea5b0c3bf59495f5ad50268dc88b253bb60e4f47dcca10 oid sha256:20f8ace2bb54a25e6044e6b63f09378b1ad9d6b8e0a8690aadb33d07aef32b2b
size 23217 size 23595

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:42a9af1521e18b0edcd05743b9dc598324c68657afa293e304576348598f4b7b oid sha256:e8e40d09036dfab63b0ce06886a4951958a00e4aa54cd8760201c71b32da0e60
size 24492 size 24895

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:2c51ecde00a75a74fb6b654f58f8c6507fe1af50f5f996c27bafa355d01f3f58 oid sha256:1535ae46ff8d99e1205966bee0c7a0455505f4ab4499805350f7aeff4931a3e8
size 42097 size 42380

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:c398ef341651bd7b0b067183b0b6566c515df3204512fd8f32df5abfe71f2e2e oid sha256:e13e3252a9550a34dbf35c2f93f7bd19168597556f402bb8c3a4cf02c34b3603
size 38450 size 38850

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:6d731d3b312cd4535d953365b1de7817c2c6d07c1ae460567f3e333cd00f8d2e oid sha256:1fec4bf898d1edc02c93cdf07dea16395cbb18779b2990fb234ce1f5d957aa2d
size 37142 size 42228

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:323370a1128d2629752159610719fd49de99a448f0278bdcce85f4f836bf3ef5 oid sha256:ddf2b23bd2990e6c30e5afada8a552af02aeaf5168f47fc8dd9dfd3a3b948c06
size 32384 size 38584

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:2cb056d627f504947058b3512873e91a792c98d6cc485d4d866fd63589c957bc oid sha256:b3af87959334905f1de9e014978dafbd2fa5834db9c3296bc5353218bc09a3f5
size 40073 size 46207

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:626d47274ead934c9c4d2831d14abc011954a627d63dac7193d881826371654c oid sha256:d26dfb3d8efcc93b7e0f25cb2067b5288edba8a9a35edc3b74863f3cd2962ce5
size 40688 size 46820

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:a008fa7c1f333a1ea380d6d7b2401bd3cc3ac70e32c2618359640d73962c31a2 oid sha256:b5816a7b77f64889d51988d286a05be318930076972b7a29c7506bedf091aebd
size 40874 size 41107

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:f739091f305037dc09fb7b6a083a30172f45c576a2ccbc08b4de1d97c37523d6 oid sha256:e765b16cfe3beca19b9f3f256512e6048a7cc22b0344f5e57d655409c2262156
size 22569 size 22893

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:320fecc889eb76fb0201dec4c299d2fd82f6a21e48544f5d45e537bec49fa3b6 oid sha256:16699f8b48a72e2336a83e2fb80cd28cdc5b7b4b5a2e065767f2d6416a372b96
size 23870 size 24204

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:a008fa7c1f333a1ea380d6d7b2401bd3cc3ac70e32c2618359640d73962c31a2 oid sha256:b5816a7b77f64889d51988d286a05be318930076972b7a29c7506bedf091aebd
size 40874 size 41107

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:d077bd218282f4d5d17d319efad0b921c7e3a17e94bee4fa220390f4988d7295 oid sha256:454d58e8c2ec04dff4ba7410e284b4b7e5439df68996f42be6181036d799f739
size 37601 size 37916

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:5d87afe654be2a6d0cfb9a1fe5a900f01809dacf23f0639b87cfd08e90650e09 oid sha256:d3420774cfe02db1e94c4bc71d41de8ab754c9de96ab8283bd8e6f7555f85ce2
size 36161 size 40908

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:19f1f3eb197a48bcd1ec0ab3acc1d46ac2597fb3ffd325f221c3a87a2ae56b6b oid sha256:082dff0fdb2c9d6de2fcbdd059b3f1d3859f2265df5fd92f9c74911a5852fcfc
size 31689 size 37528

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:08c9db896d4de76c9466675bad777591985e9b9d6d39dfae2711a95ae6de6be7 oid sha256:14ea71faf7394d985620c36093a3015b23fafd6657213ebf243ba660ff99bea2
size 39285 size 44943

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:fcda36810020f172928338753ad986294b17ff705b1720d3f358ca471a8f8fb1 oid sha256:3aef62da412a0dea0108fc4f2edb84acb3e40eab6d44e69b74e9dee3689d9725
size 39930 size 45566

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:4db4fc94f8c80bc45d241ccb8e67b18f687b2fb00fef5f4c7de5fbfbc07ede05 oid sha256:caa57bfcdb7f285e0b3e82844d83fb0b525d56ddb627c202f8d2eccdb5a79d84
size 26551 size 26912

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ca7819814008b5365e1c11cfd530587277fa8636e183eae35464c627c0a1d174 oid sha256:0514ecaaf5a91487316e36a1549049b51e50dee20eed90bc9542a6e8672fbdb7
size 25163 size 25552

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:db7af309219038951c742535c24f2bf6995438a3e9850bd9f69aa8ac2636cbf5 oid sha256:6c7de8a2a9ef5374d56fc641ac71d25be186082fa7b5a6ed979def8e3f59808e
size 33037 size 33985

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:da0a3a6b89ba22da85d8834a8b9dbc4216ee0085b7ef6f8e55203e0abb789b57 oid sha256:fb0c099c3480108b8ce842618123e95e6f302a58a56633f2ed91ed96ad8639da
size 23203 size 27074

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:d2993f8eb15e1d7dd869bad474a954db52a2b8f9769f4d1d3f0c6ab0449b7fbe oid sha256:8834781b2e2d5d4811b0ff030d0c741d08c5659d94711d6d550f81c4b0d47ebe
size 20421 size 25382

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:2cb056d627f504947058b3512873e91a792c98d6cc485d4d866fd63589c957bc oid sha256:b3af87959334905f1de9e014978dafbd2fa5834db9c3296bc5353218bc09a3f5
size 40073 size 46207

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:8ee603c36dea38a3adda22c45294a45225e44f9638f7292159019993aafd1ec7 oid sha256:465e7198dd7773d5dbd953cf3022e35f513add880d68af161a70df5ddfd00b17
size 25902 size 26234

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:bc1980e0ce3bc900f6703fe4f5f12eabbcc351b0aca873215e414af86125a0fd oid sha256:d2e530f6abdcbaf236f60aadcecaa97de715f39473069fa164c894ef0f899c35
size 24521 size 24852

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:d4d968e867119149bb7cfcfdb2c993603c60b427f4f3810ecf9784198c6804b6 oid sha256:96c4965a16f7c0d246586941230799cb4dae61ad18120420df11ea744e0730ec
size 32158 size 32993

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:352e0673fd2802c418657e9a0532a504d736234f28df1119437e3035dde06804 oid sha256:8ef0a07a8d811cb6fb59160b0003cabbf6d3437a41f912dc31b63851be549e2d
size 22685 size 26222

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:a477acdebee14e91f9e15ef9f10f318ae0fcb081a0f6bdacb91c68e5d9234b30 oid sha256:e08c58571c5252779e3dd783f0c60768e408260dc901cf6310a927b3da948172
size 20011 size 24619

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:08c9db896d4de76c9466675bad777591985e9b9d6d39dfae2711a95ae6de6be7 oid sha256:14ea71faf7394d985620c36093a3015b23fafd6657213ebf243ba660ff99bea2
size 39285 size 44943

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:3ae5556afe4e26f417c598e116dee6c7c913fe668a15ec2d98d2f1ed81b5371f oid sha256:29c51a698521fcc83604aafed9747c92b84f4077e7ea79ba35ce8a07b991fbcd
size 14269 size 12563

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:7ec9f20978244f8b933140f074bf586a174b9448a602294b95f4773ee7758323 oid sha256:f899705e82a54ee0525fd36dd5993538c51dcc9fa0b224a820407bd22827b33c
size 14406 size 12724