Merge pull request #5981 from element-hq/feature/bma/iterateOnVerificationScreen
Iterate on verification screen
This commit is contained in:
@@ -21,10 +21,8 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.ProgressBarRangeInfo
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.focused
|
||||
import androidx.compose.ui.semantics.progressBarRangeInfo
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
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.PreviewsDayNight
|
||||
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.TextButton
|
||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
@@ -73,11 +70,7 @@ fun IncomingVerificationView(
|
||||
TopAppBar(
|
||||
title = {},
|
||||
navigationIcon = {
|
||||
when {
|
||||
step is Step.Initial && !step.isWaiting -> Unit
|
||||
step is Step.Completed -> Unit
|
||||
else -> BackButton(onClick = { state.eventSink(IncomingVerificationViewEvents.GoBack) })
|
||||
}
|
||||
BackButton(onClick = { state.eventSink(IncomingVerificationViewEvents.GoBack) })
|
||||
},
|
||||
colors = topAppBarColors(containerColor = Color.Transparent),
|
||||
)
|
||||
@@ -103,19 +96,11 @@ fun IncomingVerificationView(
|
||||
private fun IncomingVerificationHeader(step: Step, request: VerificationRequest.Incoming) {
|
||||
val iconStyle = when (step) {
|
||||
Step.Canceled -> BigIcon.Style.AlertSolid
|
||||
is Step.Initial -> if (step.isWaiting) {
|
||||
BigIcon.Style.Loading
|
||||
} else {
|
||||
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.Initial -> when (request) {
|
||||
is VerificationRequest.Incoming.OtherSession -> BigIcon.Style.Default(CompoundIcons.Devices())
|
||||
is VerificationRequest.Incoming.User -> BigIcon.Style.Default(CompoundIcons.UserProfileSolid())
|
||||
}
|
||||
is Step.Verifying -> BigIcon.Style.Default(CompoundIcons.ReactionSolid())
|
||||
Step.Completed -> BigIcon.Style.SuccessSolid
|
||||
Step.Failure -> BigIcon.Style.AlertSolid
|
||||
}
|
||||
@@ -159,10 +144,6 @@ private fun IncomingVerificationHeader(step: Step, request: VerificationRequest.
|
||||
.semantics(mergeDescendants = true) {
|
||||
contentDescription = timeLimitMessage
|
||||
focused = true
|
||||
if (iconStyle == BigIcon.Style.Loading) {
|
||||
// Same code than Modifier.progressSemantics()
|
||||
progressBarRangeInfo = ProgressBarRangeInfo.Indeterminate
|
||||
}
|
||||
}
|
||||
.focusable(),
|
||||
iconStyle = iconStyle,
|
||||
@@ -185,7 +166,7 @@ private fun IncomingVerificationContent(
|
||||
|
||||
@Composable
|
||||
private fun ContentInitial(
|
||||
initialIncoming: Step.Initial,
|
||||
stepInitial: Step.Initial,
|
||||
request: VerificationRequest.Incoming,
|
||||
) {
|
||||
when (request) {
|
||||
@@ -195,9 +176,9 @@ private fun ContentInitial(
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp),
|
||||
) {
|
||||
SessionDetailsView(
|
||||
deviceName = initialIncoming.deviceDisplayName,
|
||||
deviceId = initialIncoming.deviceId,
|
||||
signInFormattedTimestamp = initialIncoming.formattedSignInTime,
|
||||
deviceName = stepInitial.deviceDisplayName,
|
||||
deviceId = stepInitial.deviceId,
|
||||
signInFormattedTimestamp = stepInitial.formattedSignInTime,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
@@ -227,48 +208,42 @@ private fun ContentInitial(
|
||||
private fun IncomingVerificationBottomMenu(
|
||||
state: IncomingVerificationState,
|
||||
) {
|
||||
val step = state.step
|
||||
val eventSink = state.eventSink
|
||||
|
||||
when (step) {
|
||||
when (val step = state.step) {
|
||||
is Step.Initial -> {
|
||||
if (step.isWaiting) {
|
||||
// Show nothing
|
||||
} else {
|
||||
VerificationBottomMenu {
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(CommonStrings.action_start_verification),
|
||||
onClick = { eventSink(IncomingVerificationViewEvents.StartVerification) },
|
||||
)
|
||||
TextButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(CommonStrings.action_ignore),
|
||||
onClick = { eventSink(IncomingVerificationViewEvents.IgnoreVerification) },
|
||||
)
|
||||
}
|
||||
VerificationBottomMenu {
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(CommonStrings.action_start_verification),
|
||||
enabled = !step.isWaiting,
|
||||
showProgress = step.isWaiting,
|
||||
onClick = { eventSink(IncomingVerificationViewEvents.StartVerification) },
|
||||
)
|
||||
TextButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(CommonStrings.action_ignore),
|
||||
enabled = !step.isWaiting,
|
||||
onClick = { eventSink(IncomingVerificationViewEvents.IgnoreVerification) },
|
||||
)
|
||||
}
|
||||
}
|
||||
is Step.Verifying -> {
|
||||
if (step.isWaiting) {
|
||||
// Add invisible buttons to keep the same screen layout
|
||||
VerificationBottomMenu {
|
||||
InvisibleButton()
|
||||
InvisibleButton()
|
||||
}
|
||||
} else {
|
||||
VerificationBottomMenu {
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(R.string.screen_session_verification_they_match),
|
||||
onClick = { eventSink(IncomingVerificationViewEvents.ConfirmVerification) },
|
||||
)
|
||||
TextButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(R.string.screen_session_verification_they_dont_match),
|
||||
onClick = { eventSink(IncomingVerificationViewEvents.DeclineVerification) },
|
||||
)
|
||||
}
|
||||
VerificationBottomMenu {
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(R.string.screen_session_verification_they_match),
|
||||
enabled = !step.isWaiting,
|
||||
showProgress = step.isWaiting,
|
||||
onClick = {
|
||||
eventSink(IncomingVerificationViewEvents.ConfirmVerification)
|
||||
},
|
||||
)
|
||||
TextButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(R.string.screen_session_verification_they_dont_match),
|
||||
enabled = !step.isWaiting,
|
||||
onClick = { eventSink(IncomingVerificationViewEvents.DeclineVerification) },
|
||||
)
|
||||
}
|
||||
}
|
||||
Step.Canceled,
|
||||
@@ -278,7 +253,9 @@ private fun IncomingVerificationBottomMenu(
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(CommonStrings.action_done),
|
||||
onClick = { eventSink(IncomingVerificationViewEvents.GoBack) },
|
||||
onClick = {
|
||||
eventSink(IncomingVerificationViewEvents.GoBack)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,10 +24,8 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.ProgressBarRangeInfo
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.focused
|
||||
import androidx.compose.ui.semantics.progressBarRangeInfo
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
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.ui.VerificationBottomMenu
|
||||
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.pages.HeaderFooterPage
|
||||
import io.element.android.libraries.designsystem.components.BigIcon
|
||||
@@ -96,20 +93,18 @@ fun OutgoingVerificationView(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {},
|
||||
navigationIcon = if (step != Step.Completed) {
|
||||
{ BackButton(onClick = ::cancelOrResetFlow) }
|
||||
} else {
|
||||
{}
|
||||
navigationIcon = {
|
||||
BackButton(onClick = ::cancelOrResetFlow)
|
||||
},
|
||||
colors = topAppBarColors(containerColor = Color.Transparent)
|
||||
colors = topAppBarColors(containerColor = Color.Transparent),
|
||||
)
|
||||
},
|
||||
header = {
|
||||
OutgoingVerificationHeader(step = step, request = state.request)
|
||||
},
|
||||
footer = {
|
||||
OutgoingVerificationViewBottomMenu(
|
||||
screenState = state,
|
||||
OutgoingVerificationBottomMenu(
|
||||
state = state,
|
||||
onCancelClick = ::cancelOrResetFlow,
|
||||
onContinueClick = onFinish,
|
||||
)
|
||||
@@ -117,7 +112,7 @@ fun OutgoingVerificationView(
|
||||
isScrollable = true,
|
||||
) {
|
||||
OutgoingVerificationContent(
|
||||
flowState = step,
|
||||
step = step,
|
||||
request = state.request,
|
||||
onLearnMoreClick = onLearnMoreClick,
|
||||
)
|
||||
@@ -129,20 +124,16 @@ fun OutgoingVerificationView(
|
||||
private fun OutgoingVerificationHeader(step: Step, request: VerificationRequest.Outgoing) {
|
||||
val iconStyle = when (step) {
|
||||
Step.Loading -> error("Should not happen")
|
||||
Step.AwaitingOtherDeviceResponse,
|
||||
Step.Initial -> when (request) {
|
||||
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.Ready -> BigIcon.Style.Default(CompoundIcons.ReactionSolid())
|
||||
Step.Completed -> BigIcon.Style.SuccessSolid
|
||||
is Step.Verifying -> {
|
||||
if (step.state is AsyncData.Loading<Unit>) {
|
||||
BigIcon.Style.Loading
|
||||
} else {
|
||||
BigIcon.Style.Default(CompoundIcons.ReactionSolid())
|
||||
}
|
||||
BigIcon.Style.Default(CompoundIcons.ReactionSolid())
|
||||
}
|
||||
is Step.Exit -> return
|
||||
}
|
||||
@@ -201,10 +192,6 @@ private fun OutgoingVerificationHeader(step: Step, request: VerificationRequest.
|
||||
.semantics(mergeDescendants = true) {
|
||||
contentDescription = timeLimitMessage
|
||||
focused = true
|
||||
if (iconStyle == BigIcon.Style.Loading) {
|
||||
// Same code than Modifier.progressSemantics()
|
||||
progressBarRangeInfo = ProgressBarRangeInfo.Indeterminate
|
||||
}
|
||||
}
|
||||
.focusable(),
|
||||
iconStyle = iconStyle,
|
||||
@@ -215,20 +202,16 @@ private fun OutgoingVerificationHeader(step: Step, request: VerificationRequest.
|
||||
|
||||
@Composable
|
||||
private fun OutgoingVerificationContent(
|
||||
flowState: Step,
|
||||
step: Step,
|
||||
request: VerificationRequest.Outgoing,
|
||||
onLearnMoreClick: () -> Unit,
|
||||
) {
|
||||
when (flowState) {
|
||||
is Step.Initial -> {
|
||||
when (request) {
|
||||
is VerificationRequest.Outgoing.CurrentSession -> Unit
|
||||
is VerificationRequest.Outgoing.User -> ContentInitial(onLearnMoreClick)
|
||||
}
|
||||
}
|
||||
is Step.Verifying -> {
|
||||
VerificationContentVerifying(flowState.data)
|
||||
when (step) {
|
||||
is Step.Initial -> when (request) {
|
||||
is VerificationRequest.Outgoing.CurrentSession -> Unit
|
||||
is VerificationRequest.Outgoing.User -> ContentInitial(onLearnMoreClick)
|
||||
}
|
||||
is Step.Verifying -> VerificationContentVerifying(step.data)
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
@@ -252,23 +235,23 @@ private fun ContentInitial(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OutgoingVerificationViewBottomMenu(
|
||||
screenState: OutgoingVerificationState,
|
||||
private fun OutgoingVerificationBottomMenu(
|
||||
state: OutgoingVerificationState,
|
||||
onCancelClick: () -> Unit,
|
||||
onContinueClick: () -> Unit,
|
||||
) {
|
||||
val verificationViewState = screenState.step
|
||||
val eventSink = screenState.eventSink
|
||||
|
||||
val isVerifying = (verificationViewState as? Step.Verifying)?.state is AsyncData.Loading<Unit>
|
||||
|
||||
when (verificationViewState) {
|
||||
val eventSink = state.eventSink
|
||||
when (val step = state.step) {
|
||||
Step.Loading -> error("Should not happen")
|
||||
is Step.AwaitingOtherDeviceResponse,
|
||||
is Step.Initial -> {
|
||||
VerificationBottomMenu {
|
||||
val isWaiting = step is Step.AwaitingOtherDeviceResponse
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(CommonStrings.action_start_verification),
|
||||
enabled = !isWaiting,
|
||||
showProgress = isWaiting,
|
||||
onClick = { eventSink(OutgoingVerificationViewEvents.RequestVerification) },
|
||||
)
|
||||
InvisibleButton()
|
||||
@@ -298,30 +281,26 @@ private fun OutgoingVerificationViewBottomMenu(
|
||||
)
|
||||
}
|
||||
}
|
||||
is Step.AwaitingOtherDeviceResponse -> Unit
|
||||
is Step.Verifying -> {
|
||||
if (isVerifying) {
|
||||
// Add invisible buttons to keep the same screen layout
|
||||
VerificationBottomMenu {
|
||||
InvisibleButton()
|
||||
InvisibleButton()
|
||||
}
|
||||
} else {
|
||||
VerificationBottomMenu {
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(R.string.screen_session_verification_they_match),
|
||||
onClick = {
|
||||
eventSink(OutgoingVerificationViewEvents.ConfirmVerification)
|
||||
},
|
||||
)
|
||||
|
||||
TextButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(R.string.screen_session_verification_they_dont_match),
|
||||
onClick = { eventSink(OutgoingVerificationViewEvents.DeclineVerification) },
|
||||
)
|
||||
}
|
||||
val isVerifying = step.state.isLoading()
|
||||
VerificationBottomMenu {
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(R.string.screen_session_verification_they_match),
|
||||
enabled = !isVerifying,
|
||||
showProgress = isVerifying,
|
||||
onClick = {
|
||||
eventSink(OutgoingVerificationViewEvents.ConfirmVerification)
|
||||
},
|
||||
)
|
||||
TextButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(R.string.screen_session_verification_they_dont_match),
|
||||
enabled = !isVerifying,
|
||||
onClick = {
|
||||
eventSink(OutgoingVerificationViewEvents.DeclineVerification)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
is Step.Completed -> {
|
||||
@@ -334,7 +313,7 @@ private fun OutgoingVerificationViewBottomMenu(
|
||||
InvisibleButton()
|
||||
}
|
||||
}
|
||||
is Step.Exit -> return
|
||||
is Step.Exit -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
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.ui.strings.CommonStrings
|
||||
|
||||
@@ -82,11 +81,6 @@ object BigIcon {
|
||||
* A success style with a tinted background.
|
||||
*/
|
||||
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.AlertSolid -> ElementTheme.colors.bgCriticalSubtle
|
||||
Style.SuccessSolid -> ElementTheme.colors.bgSuccessSubtle
|
||||
Style.Loading -> ElementTheme.colors.bgSubtleSecondary
|
||||
}
|
||||
Box(
|
||||
modifier = modifier
|
||||
@@ -119,52 +112,39 @@ object BigIcon {
|
||||
.background(backgroundColor),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
if (style is Style.Loading) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(27.dp),
|
||||
color = ElementTheme.colors.iconSecondary,
|
||||
trackColor = Color.Transparent,
|
||||
strokeWidth = 3.dp,
|
||||
)
|
||||
} 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 icon = when (style) {
|
||||
is Style.Default -> style.vectorIcon
|
||||
Style.Alert,
|
||||
Style.AlertSolid -> CompoundIcons.ErrorSolid()
|
||||
Style.Success,
|
||||
Style.SuccessSolid -> CompoundIcons.CheckCircleSolid()
|
||||
}
|
||||
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.Success,
|
||||
BigIcon.Style.SuccessSolid,
|
||||
BigIcon.Style.Loading,
|
||||
)
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user