Merge pull request #4926 from element-hq/feature/bma/roomListHeader
Remove bloom effect and replace by linear gradient
This commit is contained in:
commit
f794117047
305 changed files with 893 additions and 1436 deletions
|
|
@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.Column
|
|||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -22,7 +21,6 @@ import androidx.compose.material3.rememberTopAppBarState
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
|
|
@ -30,14 +28,9 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalInspectionMode
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.heading
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.appconfig.RoomListConfig
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
|
|
@ -51,12 +44,10 @@ 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.AvatarSize
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarType
|
||||
import io.element.android.libraries.designsystem.components.avatarBloom
|
||||
import io.element.android.libraries.designsystem.modifiers.backgroundVerticalGradient
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.text.applyScaleDown
|
||||
import io.element.android.libraries.designsystem.text.roundToPx
|
||||
import io.element.android.libraries.designsystem.text.toDp
|
||||
import io.element.android.libraries.designsystem.text.toSp
|
||||
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
|
||||
import io.element.android.libraries.designsystem.theme.components.DropdownMenu
|
||||
|
|
@ -73,8 +64,6 @@ import io.element.android.libraries.testtags.TestTags
|
|||
import io.element.android.libraries.testtags.testTag
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
private val avatarBloomSize = 430.dp
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun RoomListTopBar(
|
||||
|
|
@ -126,25 +115,13 @@ private fun DefaultRoomListTopBar(
|
|||
canReportBug: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
// We need this to manually clip the top app bar in preview mode
|
||||
val previewAppBarHeight = if (LocalInspectionMode.current) {
|
||||
112.dp.roundToPx()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val collapsedFraction = scrollBehavior.state.collapsedFraction
|
||||
var appBarHeight by remember {
|
||||
mutableIntStateOf(previewAppBarHeight ?: 0)
|
||||
}
|
||||
|
||||
val avatarData by remember(matrixUser) {
|
||||
derivedStateOf {
|
||||
matrixUser.getAvatarData(size = AvatarSize.CurrentUserTopBar)
|
||||
}
|
||||
}
|
||||
|
||||
val statusBarPadding = with(LocalDensity.current) { WindowInsets.statusBars.getTop(this).toDp() }
|
||||
|
||||
Box(modifier = modifier) {
|
||||
val collapsedTitleTextStyle = ElementTheme.typography.aliasScreenTitle
|
||||
val expandedTitleTextStyle = ElementTheme.typography.fontHeadingLgBold.copy(
|
||||
|
|
@ -160,40 +137,13 @@ private fun DefaultRoomListTopBar(
|
|||
titleLarge = collapsedTitleTextStyle
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.onSizeChanged {
|
||||
appBarHeight = it.height
|
||||
}
|
||||
.avatarBloom(
|
||||
avatarData = avatarData,
|
||||
background = if (ElementTheme.isLightTheme) {
|
||||
// Workaround to display a very subtle bloom for avatars with very soft colors
|
||||
Color(0xFFF9F9F9)
|
||||
} else {
|
||||
ElementTheme.colors.bgCanvasDefault
|
||||
},
|
||||
blurSize = DpSize(avatarBloomSize, avatarBloomSize),
|
||||
offset = DpOffset(24.dp, 24.dp + statusBarPadding),
|
||||
clipToSize = if (appBarHeight > 0) {
|
||||
DpSize(
|
||||
avatarBloomSize,
|
||||
appBarHeight.toDp()
|
||||
)
|
||||
} else {
|
||||
DpSize.Unspecified
|
||||
},
|
||||
bottomSoftEdgeColor = ElementTheme.colors.bgCanvasDefault,
|
||||
bottomSoftEdgeAlpha = if (displayFilters) {
|
||||
1f
|
||||
} else {
|
||||
1f - collapsedFraction
|
||||
},
|
||||
alpha = if (areSearchResultsDisplayed) 0f else 1f,
|
||||
)
|
||||
.statusBarsPadding(),
|
||||
) {
|
||||
Column {
|
||||
MediumTopAppBar(
|
||||
modifier = Modifier
|
||||
.backgroundVerticalGradient(
|
||||
isVisible = !areSearchResultsDisplayed,
|
||||
)
|
||||
.statusBarsPadding(),
|
||||
colors = TopAppBarDefaults.mediumTopAppBarColors(
|
||||
containerColor = Color.Transparent,
|
||||
scrolledContainerColor = Color.Transparent,
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
|||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
|
||||
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionEvent
|
||||
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState
|
||||
import io.element.android.libraries.designsystem.modifiers.subtleColorStops
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.text.toPx
|
||||
import io.element.android.libraries.designsystem.theme.LocalBuildMeta
|
||||
import io.element.android.libraries.designsystem.theme.highlightedMessageBackgroundColor
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
|
@ -206,23 +206,20 @@ internal fun TimelineItemRow(
|
|||
@Suppress("ModifierComposable")
|
||||
@Composable
|
||||
private fun Modifier.focusedEvent(
|
||||
focusedEventOffset: Dp
|
||||
focusedEventOffset: Dp,
|
||||
isEnterpriseBuild: Boolean = LocalBuildMeta.current.isEnterpriseBuild,
|
||||
): Modifier {
|
||||
val highlightedLineColor = ElementTheme.colors.textActionAccent
|
||||
val gradientFirstColor = if (LocalBuildMeta.current.isEnterpriseBuild) {
|
||||
ElementTheme.colors.textActionAccent.copy(alpha = 0.125f)
|
||||
val highlightedLineColor = if (isEnterpriseBuild) {
|
||||
ElementTheme.colors.textActionAccent
|
||||
} else {
|
||||
ElementTheme.colors.highlightedMessageBackgroundColor
|
||||
ElementTheme.colors.borderAccentSubtle
|
||||
}
|
||||
val gradientColors = listOf(
|
||||
gradientFirstColor,
|
||||
ElementTheme.colors.bgCanvasDefault,
|
||||
)
|
||||
val gradientColors = subtleColorStops(isEnterpriseBuild)
|
||||
val verticalOffset = focusedEventOffset.toPx()
|
||||
val verticalRatio = 0.7f
|
||||
return drawWithCache {
|
||||
val brush = Brush.verticalGradient(
|
||||
colors = gradientColors,
|
||||
colorStops = gradientColors,
|
||||
endY = size.height * verticalRatio,
|
||||
)
|
||||
onDrawBehind {
|
||||
|
|
@ -251,3 +248,18 @@ internal fun FocusedEventPreview() = ElementPreview {
|
|||
.focusedEvent(0.dp),
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun FocusedEventEnterprisePreview() = ElementPreview {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.fillMaxWidth()
|
||||
.height(160.dp)
|
||||
.focusedEvent(
|
||||
focusedEventOffset = 0.dp,
|
||||
isEnterpriseBuild = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,10 +14,6 @@ import io.element.android.compound.tokens.generated.CompoundIcons
|
|||
import io.element.android.libraries.designsystem.components.Badge
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.badgeNegativeBackgroundColor
|
||||
import io.element.android.libraries.designsystem.theme.badgeNegativeContentColor
|
||||
import io.element.android.libraries.designsystem.theme.badgeNeutralBackgroundColor
|
||||
import io.element.android.libraries.designsystem.theme.badgeNeutralContentColor
|
||||
|
||||
object MatrixBadgeAtom {
|
||||
data class MatrixBadgeData(
|
||||
|
|
@ -39,21 +35,21 @@ object MatrixBadgeAtom {
|
|||
) {
|
||||
val backgroundColor = when (data.type) {
|
||||
Type.Positive -> ElementTheme.colors.bgBadgeAccent
|
||||
Type.Neutral -> ElementTheme.colors.badgeNeutralBackgroundColor
|
||||
Type.Negative -> ElementTheme.colors.badgeNegativeBackgroundColor
|
||||
Type.Neutral -> ElementTheme.colors.bgBadgeDefault
|
||||
Type.Negative -> ElementTheme.colors.bgCriticalSubtle
|
||||
Type.Info -> ElementTheme.colors.bgBadgeInfo
|
||||
}
|
||||
val textColor = when (data.type) {
|
||||
Type.Positive -> ElementTheme.colors.textBadgeAccent
|
||||
Type.Neutral -> ElementTheme.colors.badgeNeutralContentColor
|
||||
Type.Negative -> ElementTheme.colors.badgeNegativeContentColor
|
||||
Type.Neutral -> ElementTheme.colors.textPrimary
|
||||
Type.Negative -> ElementTheme.colors.textCriticalPrimary
|
||||
Type.Info -> ElementTheme.colors.textBadgeInfo
|
||||
}
|
||||
val iconColor = when (data.type) {
|
||||
Type.Positive -> ElementTheme.colors.textBadgeAccent
|
||||
Type.Neutral -> ElementTheme.colors.iconSecondary
|
||||
Type.Positive -> ElementTheme.colors.iconAccentPrimary
|
||||
Type.Neutral -> ElementTheme.colors.iconPrimary
|
||||
Type.Negative -> ElementTheme.colors.iconCriticalPrimary
|
||||
Type.Info -> ElementTheme.colors.textBadgeInfo
|
||||
Type.Info -> ElementTheme.colors.iconInfoPrimary
|
||||
}
|
||||
Badge(
|
||||
text = data.text,
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ import androidx.compose.ui.graphics.LinearGradientShader
|
|||
import androidx.compose.ui.graphics.ShaderBrush
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.libraries.designsystem.components.drawWithLayer
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.utils.drawWithLayer
|
||||
|
||||
/**
|
||||
* Gradient background for FTUE (onboarding) screens.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.colors
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
fun gradientActionColors(): List<Color> = listOf(
|
||||
ElementTheme.colors.gradientActionStop1,
|
||||
ElementTheme.colors.gradientActionStop2,
|
||||
ElementTheme.colors.gradientActionStop3,
|
||||
ElementTheme.colors.gradientActionStop4,
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
fun gradientSubtleColors(): List<Color> = listOf(
|
||||
ElementTheme.colors.gradientSubtleStop1,
|
||||
ElementTheme.colors.gradientSubtleStop2,
|
||||
ElementTheme.colors.gradientSubtleStop3,
|
||||
ElementTheme.colors.gradientSubtleStop4,
|
||||
ElementTheme.colors.gradientSubtleStop5,
|
||||
ElementTheme.colors.gradientSubtleStop6,
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
fun gradientInfoColors(): List<Color> = listOf(
|
||||
ElementTheme.colors.gradientInfoStop1,
|
||||
ElementTheme.colors.gradientInfoStop2,
|
||||
ElementTheme.colors.gradientInfoStop3,
|
||||
ElementTheme.colors.gradientInfoStop4,
|
||||
ElementTheme.colors.gradientInfoStop5,
|
||||
ElementTheme.colors.gradientInfoStop6,
|
||||
)
|
||||
|
|
@ -1,578 +0,0 @@
|
|||
/*
|
||||
* Copyright 2023, 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.components
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Typeface
|
||||
import android.os.Build
|
||||
import android.text.TextPaint
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.geometry.center
|
||||
import androidx.compose.ui.graphics.BlendMode
|
||||
import androidx.compose.ui.graphics.ClipOp
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.LinearGradientShader
|
||||
import androidx.compose.ui.graphics.Paint
|
||||
import androidx.compose.ui.graphics.Path
|
||||
import androidx.compose.ui.graphics.RadialGradientShader
|
||||
import androidx.compose.ui.graphics.ShaderBrush
|
||||
import androidx.compose.ui.graphics.asAndroidBitmap
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.clipPath
|
||||
import androidx.compose.ui.graphics.nativeCanvas
|
||||
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalFontFamilyResolver
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.rememberTextMeasurer
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.isSpecified
|
||||
import androidx.compose.ui.unit.toOffset
|
||||
import androidx.compose.ui.unit.toSize
|
||||
import coil3.SingletonImageLoader
|
||||
import coil3.request.ImageRequest
|
||||
import coil3.request.allowHardware
|
||||
import coil3.toBitmap
|
||||
import com.airbnb.android.showkase.annotation.ShowkaseComposable
|
||||
import com.vanniktech.blurhash.BlurHash
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
|
||||
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.AvatarSize
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarType
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.text.toDp
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.MediumTopAppBar
|
||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Default bloom configuration values.
|
||||
*/
|
||||
object BloomDefaults {
|
||||
/**
|
||||
* Number of components to use with BlurHash to generate the blur effect.
|
||||
* Larger values mean more detailed blurs.
|
||||
*/
|
||||
const val HASH_COMPONENTS = 4
|
||||
const val ENCODE_SIZE_PX = 20
|
||||
const val DECODE_SIZE_PX = 5
|
||||
|
||||
/** Default bloom layers. */
|
||||
@Composable
|
||||
fun defaultLayers() = persistentListOf(
|
||||
// Bottom layer
|
||||
if (ElementTheme.isLightTheme) {
|
||||
BloomLayer(0.2f, BlendMode.Hardlight)
|
||||
} else {
|
||||
BloomLayer(0.5f, BlendMode.Exclusion)
|
||||
},
|
||||
// Top layer
|
||||
BloomLayer(if (ElementTheme.isLightTheme) 0.8f else 0.2f, BlendMode.Color),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Bloom layer configuration.
|
||||
* @param alpha The alpha value to apply to the layer.
|
||||
* @param blendMode The blend mode to apply to the layer.
|
||||
*/
|
||||
data class BloomLayer(
|
||||
val alpha: Float,
|
||||
val blendMode: BlendMode,
|
||||
)
|
||||
|
||||
/**
|
||||
* Bloom effect modifier. Applies a bloom effect to the component.
|
||||
* @param hash The BlurHash to use as the bloom source.
|
||||
* @param background The background color to use for the bloom effect. Since we use blend modes it must be non-transparent.
|
||||
* @param blurSize The size of the bloom effect. If not specified the bloom effect will be the size of the component.
|
||||
* @param offset The offset to use for the bloom effect. If not specified the bloom effect will be centered on the component.
|
||||
* @param clipToSize The size to use for clipping the bloom effect. If not specified the bloom effect will not be clipped.
|
||||
* @param layerConfiguration The configuration for the bloom layers. If not specified the default layers configuration will be used.
|
||||
* @param bottomSoftEdgeColor The color to use for the bottom soft edge. If not specified the [background] color will be used.
|
||||
* @param bottomSoftEdgeHeight The height of the bottom soft edge. If not specified the bottom soft edge will not be drawn.
|
||||
* @param bottomSoftEdgeAlpha The alpha value to apply to the bottom soft edge.
|
||||
* @param alpha The alpha value to apply to the bloom effect.
|
||||
*/
|
||||
@SuppressWarnings("ModifierComposed")
|
||||
fun Modifier.bloom(
|
||||
hash: String?,
|
||||
background: Color,
|
||||
blurSize: DpSize = DpSize.Unspecified,
|
||||
offset: DpOffset = DpOffset.Unspecified,
|
||||
clipToSize: DpSize = DpSize.Unspecified,
|
||||
layerConfiguration: ImmutableList<BloomLayer>? = null,
|
||||
bottomSoftEdgeColor: Color = background,
|
||||
bottomSoftEdgeHeight: Dp = 40.dp,
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
bottomSoftEdgeAlpha: Float = 1.0f,
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
alpha: Float = 1f,
|
||||
) = composed {
|
||||
val defaultLayers = BloomDefaults.defaultLayers()
|
||||
val layers = layerConfiguration ?: defaultLayers
|
||||
// Bloom only works on API 29+
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) return@composed this
|
||||
if (hash == null) return@composed this
|
||||
|
||||
val hashedBitmap = remember(hash) {
|
||||
BlurHash.decode(
|
||||
blurHash = hash,
|
||||
width = BloomDefaults.DECODE_SIZE_PX,
|
||||
height = BloomDefaults.DECODE_SIZE_PX,
|
||||
)?.asImageBitmap()
|
||||
} ?: return@composed this
|
||||
val density = LocalDensity.current
|
||||
val pixelSize = remember(blurSize, density) { blurSize.toIntSize(density) }
|
||||
val clipToPixelSize = remember(clipToSize, density) { clipToSize.toIntSize(density) }
|
||||
val bottomSoftEdgeHeightPixels = remember(bottomSoftEdgeHeight, density) { with(density) { bottomSoftEdgeHeight.roundToPx() } }
|
||||
val isRTL = LocalLayoutDirection.current == LayoutDirection.Rtl
|
||||
drawWithCache {
|
||||
val dstSize = if (pixelSize != IntSize.Zero) {
|
||||
pixelSize
|
||||
} else {
|
||||
IntSize(size.width.toInt(), size.height.toInt())
|
||||
}
|
||||
// Calculate where to place the center of the bloom effect
|
||||
val centerOffset = if (offset.isSpecified) {
|
||||
if (isRTL) {
|
||||
IntOffset(
|
||||
size.width.roundToInt() - offset.x.roundToPx(),
|
||||
size.height.roundToInt() - offset.y.roundToPx(),
|
||||
)
|
||||
} else {
|
||||
IntOffset(
|
||||
offset.x.roundToPx(),
|
||||
offset.y.roundToPx(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
IntOffset(
|
||||
size.center.x.toInt(),
|
||||
size.center.y.toInt(),
|
||||
)
|
||||
}
|
||||
// Calculate the offset to draw the different layers and apply clipping
|
||||
// This offset is applied to place the top left corner of the bloom effect
|
||||
val layersOffset = if (offset.isSpecified) {
|
||||
// Offsets the layers so the center of the bloom effect is at the provided offset value
|
||||
IntOffset(
|
||||
centerOffset.x - dstSize.width / 2,
|
||||
centerOffset.y - dstSize.height / 2,
|
||||
)
|
||||
} else {
|
||||
// Places the layers at the center of the component
|
||||
IntOffset.Zero
|
||||
}
|
||||
val radius = max(dstSize.width, dstSize.height).toFloat() / 2
|
||||
val circularGradientShader = RadialGradientShader(
|
||||
centerOffset.toOffset(),
|
||||
radius,
|
||||
listOf(Color.Red, Color.Transparent),
|
||||
listOf(0f, 1f)
|
||||
)
|
||||
val circularGradientBrush = ShaderBrush(circularGradientShader)
|
||||
val bottomEdgeGradient = LinearGradientShader(
|
||||
from = IntOffset(0, clipToPixelSize.height - bottomSoftEdgeHeightPixels).toOffset(),
|
||||
to = IntOffset(0, clipToPixelSize.height).toOffset(),
|
||||
listOf(Color.Transparent, bottomSoftEdgeColor),
|
||||
listOf(0f, 1f)
|
||||
)
|
||||
val bottomEdgeGradientBrush = ShaderBrush(bottomEdgeGradient)
|
||||
onDrawBehind {
|
||||
if (dstSize != IntSize.Zero) {
|
||||
val circleClipPath = Path().apply {
|
||||
addOval(Rect(centerOffset.toOffset(), radius - 1))
|
||||
}
|
||||
// Clip the external radius of bloom gradient too, otherwise we have a 1px border
|
||||
clipPath(circleClipPath, clipOp = ClipOp.Intersect) {
|
||||
// Draw the bloom layers
|
||||
drawWithLayer {
|
||||
// Clip rect to the provided size if needed
|
||||
if (clipToPixelSize != IntSize.Zero) {
|
||||
drawContext.canvas.clipRect(Rect(Offset.Zero, clipToPixelSize.toSize()), ClipOp.Intersect)
|
||||
}
|
||||
// Draw background color for blending
|
||||
drawRect(background, size = pixelSize.toSize())
|
||||
// Draw layers
|
||||
for (layer in layers) {
|
||||
drawImage(
|
||||
hashedBitmap,
|
||||
srcSize = IntSize(BloomDefaults.HASH_COMPONENTS, BloomDefaults.HASH_COMPONENTS),
|
||||
dstSize = dstSize,
|
||||
dstOffset = layersOffset,
|
||||
alpha = layer.alpha * alpha,
|
||||
blendMode = layer.blendMode,
|
||||
)
|
||||
}
|
||||
// Mask the layers erasing the outer radius using the gradient brush
|
||||
drawCircle(
|
||||
circularGradientBrush,
|
||||
radius,
|
||||
centerOffset.toOffset(),
|
||||
blendMode = BlendMode.DstIn
|
||||
)
|
||||
}
|
||||
}
|
||||
// Draw the bottom soft edge
|
||||
drawRect(
|
||||
bottomEdgeGradientBrush,
|
||||
topLeft = IntOffset(0, clipToPixelSize.height - bottomSoftEdgeHeight.roundToPx()).toOffset(),
|
||||
size = IntSize(pixelSize.width, bottomSoftEdgeHeight.roundToPx()).toSize(),
|
||||
alpha = bottomSoftEdgeAlpha
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bloom effect modifier for avatars. Applies a bloom effect to the component.
|
||||
* @param avatarData The avatar data to use as the bloom source.
|
||||
* If the avatar data has a URL it will be used as the bloom source, otherwise the initials will be used.
|
||||
* @param background The background color to use for the bloom effect. Since we use blend modes it must be non-transparent.
|
||||
* @param blurSize The size of the bloom effect. If not specified the bloom effect will be the size of the component.
|
||||
* @param offset The offset to use for the bloom effect. If not specified the bloom effect will be centered on the component.
|
||||
* @param clipToSize The size to use for clipping the bloom effect. If not specified the bloom effect will not be clipped.
|
||||
* @param bottomSoftEdgeColor The color to use for the bottom soft edge. If not specified the [background] color will be used.
|
||||
* @param bottomSoftEdgeHeight The height of the bottom soft edge. If not specified the bottom soft edge will not be drawn.
|
||||
* @param bottomSoftEdgeAlpha The alpha value to apply to the bottom soft edge.
|
||||
* @param alpha The alpha value to apply to the bloom effect.
|
||||
*/
|
||||
@SuppressWarnings("ModifierComposed")
|
||||
fun Modifier.avatarBloom(
|
||||
avatarData: AvatarData,
|
||||
background: Color,
|
||||
blurSize: DpSize = DpSize.Unspecified,
|
||||
offset: DpOffset = DpOffset.Unspecified,
|
||||
clipToSize: DpSize = DpSize.Unspecified,
|
||||
bottomSoftEdgeColor: Color = background,
|
||||
bottomSoftEdgeHeight: Dp = 40.dp,
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
bottomSoftEdgeAlpha: Float = 1.0f,
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
alpha: Float = 1f,
|
||||
) = composed {
|
||||
// Bloom only works on API 29+
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) return@composed this
|
||||
|
||||
// Request the avatar contents to use as the bloom source
|
||||
val context = LocalContext.current
|
||||
if (avatarData.url != null) {
|
||||
val painterRequest = remember(avatarData) {
|
||||
ImageRequest.Builder(context)
|
||||
.data(avatarData)
|
||||
// Allow cache and default dispatchers
|
||||
.defaults(ImageRequest.Defaults())
|
||||
// Needed to be able to read pixels from the Bitmap for the hash
|
||||
.allowHardware(false)
|
||||
// Reduce size so it loads faster for large avatars
|
||||
.size(BloomDefaults.ENCODE_SIZE_PX, BloomDefaults.ENCODE_SIZE_PX)
|
||||
.build()
|
||||
}
|
||||
|
||||
// By making it saveable, we'll 'cache' the previous bloom effect until a new one is loaded
|
||||
var blurHash by rememberSaveable(avatarData) { mutableStateOf<String?>(null) }
|
||||
LaunchedEffect(avatarData) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val bitmap = SingletonImageLoader.get(context)
|
||||
.execute(painterRequest)
|
||||
.image
|
||||
?.toBitmap()
|
||||
?: return@withContext
|
||||
blurHash = BlurHash.encode(
|
||||
bitmap = bitmap,
|
||||
componentX = BloomDefaults.HASH_COMPONENTS,
|
||||
componentY = BloomDefaults.HASH_COMPONENTS,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
bloom(
|
||||
hash = blurHash,
|
||||
background = background,
|
||||
blurSize = blurSize,
|
||||
offset = offset,
|
||||
clipToSize = clipToSize,
|
||||
bottomSoftEdgeColor = bottomSoftEdgeColor,
|
||||
bottomSoftEdgeHeight = bottomSoftEdgeHeight,
|
||||
bottomSoftEdgeAlpha = bottomSoftEdgeAlpha,
|
||||
alpha = alpha,
|
||||
)
|
||||
} else {
|
||||
// There is no URL so we'll generate an avatar with the initials and use that as the bloom source
|
||||
val avatarColors = AvatarColorsProvider.provide(avatarData.id)
|
||||
val initialsBitmap = initialsBitmap(
|
||||
width = BloomDefaults.ENCODE_SIZE_PX.toDp(),
|
||||
height = BloomDefaults.ENCODE_SIZE_PX.toDp(),
|
||||
text = avatarData.initialLetter,
|
||||
textColor = avatarColors.foreground,
|
||||
backgroundColor = avatarColors.background,
|
||||
)
|
||||
val hash = remember(avatarData, avatarColors) {
|
||||
BlurHash.encode(
|
||||
bitmap = initialsBitmap.asAndroidBitmap(),
|
||||
componentX = BloomDefaults.HASH_COMPONENTS,
|
||||
componentY = BloomDefaults.HASH_COMPONENTS,
|
||||
)
|
||||
}
|
||||
bloom(
|
||||
hash = hash,
|
||||
background = background,
|
||||
blurSize = blurSize,
|
||||
offset = offset,
|
||||
clipToSize = clipToSize,
|
||||
bottomSoftEdgeColor = bottomSoftEdgeColor,
|
||||
bottomSoftEdgeHeight = bottomSoftEdgeHeight,
|
||||
bottomSoftEdgeAlpha = bottomSoftEdgeAlpha,
|
||||
alpha = alpha,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Used to create a Bitmap version of the initials avatar
|
||||
@Composable
|
||||
private fun initialsBitmap(
|
||||
text: String,
|
||||
backgroundColor: Color,
|
||||
textColor: Color,
|
||||
width: Dp = 32.dp,
|
||||
height: Dp = 32.dp,
|
||||
): ImageBitmap = with(LocalDensity.current) {
|
||||
val backgroundPaint = remember(backgroundColor) {
|
||||
Paint().also { it.color = backgroundColor }
|
||||
}
|
||||
val resolver: FontFamily.Resolver = LocalFontFamilyResolver.current
|
||||
val fontSize = remember { height.toSp() / 2 }
|
||||
val typeface: Typeface = remember(resolver) {
|
||||
resolver.resolve(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontStyle = FontStyle.Normal,
|
||||
)
|
||||
}.value as Typeface
|
||||
val textPaint = remember(textColor, typeface) {
|
||||
TextPaint().apply {
|
||||
color = textColor.toArgb()
|
||||
textSize = fontSize.toPx()
|
||||
this.typeface = typeface
|
||||
}
|
||||
}
|
||||
val textMeasurer = rememberTextMeasurer()
|
||||
val result = remember(text) { textMeasurer.measure(text, TextStyle.Default.copy(fontSize = fontSize)) }
|
||||
val centerPx = remember(width, height) { IntOffset(width.roundToPx() / 2, height.roundToPx() / 2) }
|
||||
remember(text, width, height, backgroundColor, textColor) {
|
||||
val bitmap = Bitmap.createBitmap(width.roundToPx(), height.roundToPx(), Bitmap.Config.ARGB_8888).asImageBitmap()
|
||||
androidx.compose.ui.graphics.Canvas(bitmap).also { canvas ->
|
||||
canvas.drawCircle(centerPx.toOffset(), width.toPx() / 2, backgroundPaint)
|
||||
canvas.nativeCanvas.drawText(text, centerPx.x.toFloat() - result.size.width / 2, centerPx.y * 2f - result.size.height / 2 - 4, textPaint)
|
||||
}
|
||||
bitmap
|
||||
}
|
||||
}
|
||||
|
||||
// Translates DP sizes into pixel sizes, taking into account unspecified values
|
||||
private fun DpSize.toIntSize(density: Density) = with(density) {
|
||||
if (isSpecified) {
|
||||
IntSize(width.roundToPx(), height.roundToPx())
|
||||
} else {
|
||||
IntSize.Zero
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to draw to a canvas using layers. This allows us to apply clipping to those layers only.
|
||||
*/
|
||||
fun DrawScope.drawWithLayer(block: DrawScope.() -> Unit) {
|
||||
with(drawContext.canvas.nativeCanvas) {
|
||||
val checkPoint = saveLayer(null, null)
|
||||
block()
|
||||
restoreToCount(checkPoint)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@PreviewsDayNight
|
||||
@ShowkaseComposable(group = PreviewGroup.Bloom)
|
||||
@Composable
|
||||
internal fun BloomPreview() {
|
||||
val blurhash = "eePn{tI?xExEja}ooKWWodjtNJoKR,j@a|sBWpS3WDbGazoKWWWWj@"
|
||||
var topAppBarHeight by remember { mutableIntStateOf(-1) }
|
||||
val topAppBarState = rememberTopAppBarState()
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(topAppBarState)
|
||||
ElementPreview(
|
||||
drawableFallbackForImages = CommonDrawables.sample_avatar,
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
Box {
|
||||
MediumTopAppBar(
|
||||
modifier = Modifier
|
||||
.onSizeChanged { size ->
|
||||
topAppBarHeight = size.height
|
||||
}
|
||||
.bloom(
|
||||
hash = blurhash,
|
||||
background = ElementTheme.colors.bgCanvasDefault,
|
||||
blurSize = DpSize(430.dp, 430.dp),
|
||||
offset = DpOffset(24.dp, 24.dp),
|
||||
clipToSize = if (topAppBarHeight > 0) DpSize(430.dp, topAppBarHeight.toDp()) else DpSize.Zero,
|
||||
),
|
||||
colors = TopAppBarDefaults.largeTopAppBarColors(
|
||||
containerColor = Color.Transparent,
|
||||
scrolledContainerColor = Color.Black.copy(alpha = 0.05f),
|
||||
),
|
||||
navigationIcon = {
|
||||
Avatar(
|
||||
avatarData = AvatarData(
|
||||
id = "sample-avatar",
|
||||
name = "sample",
|
||||
url = "aURL",
|
||||
size = AvatarSize.CurrentUserTopBar,
|
||||
),
|
||||
avatarType = AvatarType.User,
|
||||
)
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = {}) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.ShareAndroid(),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text("Title")
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
}
|
||||
},
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.consumeWindowInsets(paddingValues)
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
repeat(20) {
|
||||
Text("Content", modifier = Modifier.padding(vertical = 20.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class InitialsColorIntProvider : PreviewParameterProvider<Int> {
|
||||
override val values: Sequence<Int>
|
||||
get() = sequenceOf(0, 1, 2, 3, 4, 5, 6, 7)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
@ShowkaseComposable(group = PreviewGroup.Bloom)
|
||||
internal fun BloomInitialsPreview(@PreviewParameter(InitialsColorIntProvider::class) color: Int) {
|
||||
ElementPreview {
|
||||
val avatarColors = AvatarColorsProvider.provide("$color")
|
||||
val bitmap = initialsBitmap(text = "F", backgroundColor = avatarColors.background, textColor = avatarColors.foreground)
|
||||
val hash = BlurHash.encode(
|
||||
bitmap = bitmap.asAndroidBitmap(),
|
||||
componentX = BloomDefaults.HASH_COMPONENTS,
|
||||
componentY = BloomDefaults.HASH_COMPONENTS,
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(256.dp)
|
||||
.bloom(
|
||||
hash = hash,
|
||||
background = if (ElementTheme.isLightTheme) {
|
||||
// Workaround to display a very subtle bloom for avatars with very soft colors
|
||||
Color(0xFFF9F9F9)
|
||||
} else {
|
||||
ElementTheme.colors.bgCanvasDefault
|
||||
},
|
||||
bottomSoftEdgeColor = ElementTheme.colors.bgCanvasDefault,
|
||||
blurSize = DpSize(256.dp, 256.dp),
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
.clip(CircleShape),
|
||||
painter = BitmapPainter(bitmap),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -36,10 +36,8 @@ import androidx.compose.ui.graphics.ShaderBrush
|
|||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.annotations.CoreColorToken
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.internal.DarkColorTokens
|
||||
import io.element.android.compound.tokens.generated.internal.LightColorTokens
|
||||
import io.element.android.libraries.designsystem.colors.gradientActionColors
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.LocalBuildMeta
|
||||
|
|
@ -47,7 +45,6 @@ import io.element.android.libraries.designsystem.theme.components.ButtonSize
|
|||
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
|
||||
import io.element.android.libraries.designsystem.theme.components.lowHorizontalPaddingValue
|
||||
|
||||
@OptIn(CoreColorToken::class)
|
||||
@Composable
|
||||
fun SuperButton(
|
||||
onClick: () -> Unit,
|
||||
|
|
@ -66,34 +63,21 @@ fun SuperButton(
|
|||
ButtonSize.Small -> PaddingValues(horizontal = 16.dp, vertical = 5.dp)
|
||||
}
|
||||
}
|
||||
val isLightTheme = ElementTheme.isLightTheme
|
||||
val colors = if (LocalBuildMeta.current.isEnterpriseBuild) {
|
||||
listOf(
|
||||
ElementTheme.colors.textActionAccent,
|
||||
ElementTheme.colors.textActionAccent,
|
||||
)
|
||||
} else {
|
||||
remember(isLightTheme) {
|
||||
if (isLightTheme) {
|
||||
listOf(
|
||||
LightColorTokens.colorBlue900,
|
||||
LightColorTokens.colorGreen1100,
|
||||
)
|
||||
} else {
|
||||
listOf(
|
||||
DarkColorTokens.colorBlue900,
|
||||
DarkColorTokens.colorGreen1100,
|
||||
)
|
||||
}
|
||||
}
|
||||
gradientActionColors()
|
||||
}
|
||||
|
||||
val shaderBrush = remember(colors) {
|
||||
object : ShaderBrush() {
|
||||
override fun createShader(size: Size): Shader {
|
||||
return LinearGradientShader(
|
||||
from = Offset(0f, size.height),
|
||||
to = Offset(size.width, 0f),
|
||||
from = Offset(0f, 0f),
|
||||
to = Offset(0f, size.height),
|
||||
colors = colors,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.modifiers
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.libraries.designsystem.colors.gradientSubtleColors
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.LocalBuildMeta
|
||||
|
||||
/**
|
||||
* Ref: https://www.figma.com/design/kcnHxunG1LDWXsJhaNuiHz/ER-145--Workspaces-V1?node-id=1141-24692
|
||||
*/
|
||||
@Stable
|
||||
@Composable
|
||||
fun Modifier.backgroundVerticalGradient(
|
||||
isVisible: Boolean = true,
|
||||
isEnterpriseBuild: Boolean = LocalBuildMeta.current.isEnterpriseBuild,
|
||||
): Modifier {
|
||||
if (!isVisible) return this
|
||||
return background(
|
||||
brush = Brush.verticalGradient(
|
||||
colorStops = subtleColorStops(isEnterpriseBuild),
|
||||
),
|
||||
alpha = 0.75f,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun subtleColorStops(
|
||||
isEnterpriseBuild: Boolean = LocalBuildMeta.current.isEnterpriseBuild,
|
||||
): Array<Pair<Float, Color>> {
|
||||
return buildList {
|
||||
if (isEnterpriseBuild) {
|
||||
// For enterprise builds, ensure that we are theming the gradient
|
||||
add(0f to ElementTheme.colors.textActionAccent.copy(alpha = 0.5f))
|
||||
add(0.75f to ElementTheme.colors.bgCanvasDefault)
|
||||
add(1f to Color.Transparent)
|
||||
} else {
|
||||
val colors = gradientSubtleColors()
|
||||
colors.forEachIndexed { index, color ->
|
||||
add(index.toFloat() / (colors.size - 1) to color)
|
||||
}
|
||||
}
|
||||
}.toTypedArray()
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun BackgroundVerticalGradientPreview() = ElementPreview {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(height = 100.dp)
|
||||
.backgroundVerticalGradient()
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun BackgroundVerticalGradientEnterprisePreview() = ElementPreview {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(height = 100.dp)
|
||||
.backgroundVerticalGradient(
|
||||
isEnterpriseBuild = true,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun BackgroundVerticalGradientDisabledPreview() = ElementPreview {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(height = 100.dp)
|
||||
.backgroundVerticalGradient(
|
||||
isVisible = false,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@ package io.element.android.libraries.designsystem.preview
|
|||
object PreviewGroup {
|
||||
const val AppBars = "App Bars"
|
||||
const val Avatars = "Avatars"
|
||||
const val Bloom = "Bloom"
|
||||
const val BottomSheets = "Bottom Sheets"
|
||||
const val Buttons = "Buttons"
|
||||
const val DateTimePickers = "DateTime pickers"
|
||||
|
|
|
|||
|
|
@ -52,11 +52,6 @@ val SemanticColors.messageFromOtherBackground
|
|||
val SemanticColors.progressIndicatorTrackColor
|
||||
get() = if (isLight) LightColorTokens.colorAlphaGray500 else DarkColorTokens.colorAlphaGray500
|
||||
|
||||
// This color is not present in Semantic color, so put hard-coded value for now
|
||||
@OptIn(CoreColorToken::class)
|
||||
val SemanticColors.iconSuccessPrimaryBackground
|
||||
get() = if (isLight) LightColorTokens.colorGreen300 else DarkColorTokens.colorGreen300
|
||||
|
||||
// This color is not present in Semantic color, so put hard-coded value for now
|
||||
@OptIn(CoreColorToken::class)
|
||||
val SemanticColors.bgSubtleTertiary
|
||||
|
|
@ -71,57 +66,10 @@ val SemanticColors.temporaryColorBgSpecial
|
|||
val SemanticColors.pinDigitBg
|
||||
get() = if (isLight) LightColorTokens.colorGray300 else DarkColorTokens.colorGray400
|
||||
|
||||
@OptIn(CoreColorToken::class)
|
||||
val SemanticColors.currentUserMentionPillText
|
||||
get() = if (isLight) LightColorTokens.colorGreen1100 else DarkColorTokens.colorGreen1100
|
||||
|
||||
val SemanticColors.currentUserMentionPillBackground
|
||||
get() = if (isLight) {
|
||||
// We want LightDesignTokens.colorGreenAlpha400
|
||||
Color(0x3b07b661)
|
||||
} else {
|
||||
// We want DarkDesignTokens.colorGreenAlpha500
|
||||
Color(0xff003d29)
|
||||
}
|
||||
|
||||
val SemanticColors.mentionPillText
|
||||
get() = textPrimary
|
||||
|
||||
val SemanticColors.mentionPillBackground
|
||||
get() = if (isLight) {
|
||||
// We want LightDesignTokens.colorGray400
|
||||
Color(0x1f052e61)
|
||||
} else {
|
||||
// We want DarkDesignTokens.colorGray500
|
||||
Color(0x26f4f7fa)
|
||||
}
|
||||
|
||||
@OptIn(CoreColorToken::class)
|
||||
val SemanticColors.bigCheckmarkBorderColor
|
||||
get() = if (isLight) LightColorTokens.colorGray400 else DarkColorTokens.colorGray400
|
||||
|
||||
@OptIn(CoreColorToken::class)
|
||||
val SemanticColors.highlightedMessageBackgroundColor
|
||||
get() = if (isLight) LightColorTokens.colorGreen300 else DarkColorTokens.colorGreen300
|
||||
|
||||
// Badge colors
|
||||
|
||||
@OptIn(CoreColorToken::class)
|
||||
val SemanticColors.badgeNeutralBackgroundColor
|
||||
get() = if (isLight) LightColorTokens.colorAlphaGray300 else DarkColorTokens.colorAlphaGray300
|
||||
|
||||
@OptIn(CoreColorToken::class)
|
||||
val SemanticColors.badgeNeutralContentColor
|
||||
get() = if (isLight) LightColorTokens.colorGray1100 else DarkColorTokens.colorGray1100
|
||||
|
||||
@OptIn(CoreColorToken::class)
|
||||
val SemanticColors.badgeNegativeBackgroundColor
|
||||
get() = if (isLight) LightColorTokens.colorAlphaRed300 else DarkColorTokens.colorAlphaRed300
|
||||
|
||||
@OptIn(CoreColorToken::class)
|
||||
val SemanticColors.badgeNegativeContentColor
|
||||
get() = if (isLight) LightColorTokens.colorRed1100 else DarkColorTokens.colorRed1100
|
||||
|
||||
@OptIn(CoreColorToken::class)
|
||||
val SemanticColors.pinnedMessageBannerIndicator
|
||||
get() = if (isLight) LightColorTokens.colorAlphaGray600 else DarkColorTokens.colorAlphaGray600
|
||||
|
|
@ -146,9 +94,7 @@ internal fun ColorAliasesPreview() = ElementPreview {
|
|||
"messageFromOtherBackground" to ElementTheme.colors.messageFromOtherBackground,
|
||||
"progressIndicatorTrackColor" to ElementTheme.colors.progressIndicatorTrackColor,
|
||||
"temporaryColorBgSpecial" to ElementTheme.colors.temporaryColorBgSpecial,
|
||||
"iconSuccessPrimaryBackground" to ElementTheme.colors.iconSuccessPrimaryBackground,
|
||||
"bigCheckmarkBorderColor" to ElementTheme.colors.bigCheckmarkBorderColor,
|
||||
"highlightedMessageBackgroundColor" to ElementTheme.colors.highlightedMessageBackgroundColor,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.utils
|
||||
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.nativeCanvas
|
||||
|
||||
/**
|
||||
* Helper to draw to a canvas using layers. This allows us to apply clipping to those layers only.
|
||||
*/
|
||||
fun DrawScope.drawWithLayer(block: DrawScope.() -> Unit) {
|
||||
with(drawContext.canvas.nativeCanvas) {
|
||||
val checkPoint = saveLayer(null, null)
|
||||
block()
|
||||
restoreToCount(checkPoint)
|
||||
}
|
||||
}
|
||||
|
|
@ -30,7 +30,6 @@ 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.Icon
|
||||
import io.element.android.libraries.designsystem.theme.iconSuccessPrimaryBackground
|
||||
|
||||
@Composable
|
||||
internal fun FormattingOption(
|
||||
|
|
@ -42,13 +41,13 @@ internal fun FormattingOption(
|
|||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val backgroundColor = when (state) {
|
||||
FormattingOptionState.Selected -> ElementTheme.colors.iconSuccessPrimaryBackground
|
||||
FormattingOptionState.Selected -> ElementTheme.colors.bgAccentSelected
|
||||
FormattingOptionState.Default,
|
||||
FormattingOptionState.Disabled -> Color.Transparent
|
||||
}
|
||||
|
||||
val foregroundColor = when (state) {
|
||||
FormattingOptionState.Selected -> ElementTheme.colors.iconSuccessPrimary
|
||||
FormattingOptionState.Selected -> ElementTheme.colors.iconAccentPrimary
|
||||
FormattingOptionState.Default -> ElementTheme.colors.iconSecondary
|
||||
FormattingOptionState.Disabled -> ElementTheme.colors.iconDisabled
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.LinearGradientShader
|
||||
import androidx.compose.ui.graphics.ShaderBrush
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.designsystem.colors.gradientActionColors
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
|
|
@ -62,13 +62,7 @@ internal fun SendButton(
|
|||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.size(36.dp)
|
||||
.then(
|
||||
if (canSendMessage) {
|
||||
buttonBackgroundModifier()
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
)
|
||||
.buttonBackgroundModifier(canSendMessage)
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
|
|
@ -91,27 +85,30 @@ internal fun SendButton(
|
|||
}
|
||||
}
|
||||
|
||||
private fun buttonBackgroundModifier() = Modifier.drawWithCache {
|
||||
// We have a square button, so height == width.
|
||||
val height = size.height
|
||||
val verticalGradientBrush = ShaderBrush(
|
||||
LinearGradientShader(
|
||||
from = Offset(0f, 0f),
|
||||
to = Offset(0f, height),
|
||||
colors = listOf(
|
||||
Color(0xFF79DD98),
|
||||
Color(0xFF0DBD8B),
|
||||
Color(0xFF128585),
|
||||
Color(0xFF24446B),
|
||||
@Composable
|
||||
private fun Modifier.buttonBackgroundModifier(
|
||||
canSendMessage: Boolean,
|
||||
) = then(
|
||||
if (canSendMessage) {
|
||||
val colors = gradientActionColors()
|
||||
Modifier.drawWithCache {
|
||||
val verticalGradientBrush = ShaderBrush(
|
||||
LinearGradientShader(
|
||||
from = Offset(0f, 0f),
|
||||
to = Offset(0f, size.height),
|
||||
colors = colors,
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
onDrawBehind {
|
||||
drawRect(
|
||||
brush = verticalGradientBrush,
|
||||
)
|
||||
onDrawBehind {
|
||||
drawRect(
|
||||
brush = verticalGradientBrush,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -28,10 +28,6 @@ import io.element.android.compound.theme.ElementTheme
|
|||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.text.rememberTypeface
|
||||
import io.element.android.libraries.designsystem.theme.currentUserMentionPillBackground
|
||||
import io.element.android.libraries.designsystem.theme.currentUserMentionPillText
|
||||
import io.element.android.libraries.designsystem.theme.mentionPillBackground
|
||||
import io.element.android.libraries.designsystem.theme.mentionPillText
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.di.SingleIn
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
|
|
@ -69,10 +65,10 @@ class MentionSpanTheme(val currentUserId: UserId) {
|
|||
@Suppress("ComposableNaming")
|
||||
@Composable
|
||||
fun updateStyles() {
|
||||
currentUserTextColor = ElementTheme.colors.currentUserMentionPillText.toArgb()
|
||||
currentUserBackgroundColor = ElementTheme.colors.currentUserMentionPillBackground.toArgb()
|
||||
otherTextColor = ElementTheme.colors.mentionPillText.toArgb()
|
||||
otherBackgroundColor = ElementTheme.colors.mentionPillBackground.toArgb()
|
||||
currentUserTextColor = ElementTheme.colors.textBadgeAccent.toArgb()
|
||||
currentUserBackgroundColor = ElementTheme.colors.bgBadgeAccent.toArgb()
|
||||
otherTextColor = ElementTheme.colors.textPrimary.toArgb()
|
||||
otherBackgroundColor = ElementTheme.colors.bgBadgeDefault.toArgb()
|
||||
|
||||
typeface.value = ElementTheme.typography.fontBodyLgMedium.rememberTypeface().value
|
||||
val density = LocalDensity.current
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@
|
|||
package io.element.android.tests.konsist
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.lemonappdev.konsist.api.Konsist
|
||||
import com.lemonappdev.konsist.api.ext.list.withAllAnnotationsOf
|
||||
import com.lemonappdev.konsist.api.ext.list.withName
|
||||
import com.lemonappdev.konsist.api.ext.list.withoutName
|
||||
import com.lemonappdev.konsist.api.verify.assertEmpty
|
||||
import com.lemonappdev.konsist.api.verify.assertTrue
|
||||
|
|
@ -52,95 +54,115 @@ class KonsistPreviewTest {
|
|||
}
|
||||
}
|
||||
|
||||
private val previewNameExceptions = listOf(
|
||||
"AsyncIndicatorFailurePreview",
|
||||
"AsyncIndicatorLoadingPreview",
|
||||
"BackgroundVerticalGradientDisabledPreview",
|
||||
"BackgroundVerticalGradientEnterprisePreview",
|
||||
"BackgroundVerticalGradientPreview",
|
||||
"ColorAliasesPreview",
|
||||
"DefaultRoomListTopBarWithIndicatorPreview",
|
||||
"FocusedEventEnterprisePreview",
|
||||
"FocusedEventPreview",
|
||||
"GradientFloatingActionButtonCircleShapePreview",
|
||||
"HeaderFooterPageScrollablePreview",
|
||||
"IconsCompoundPreview",
|
||||
"IconsOtherPreview",
|
||||
"MarkdownTextComposerEditPreview",
|
||||
"MatrixBadgeAtomInfoPreview",
|
||||
"MatrixBadgeAtomNegativePreview",
|
||||
"MatrixBadgeAtomNeutralPreview",
|
||||
"MatrixBadgeAtomPositivePreview",
|
||||
"MessageComposerViewVoicePreview",
|
||||
"MessagesReactionButtonAddPreview",
|
||||
"MessagesReactionButtonExtraPreview",
|
||||
"MessagesViewWithIdentityChangePreview",
|
||||
"PageTitleWithIconFullPreview",
|
||||
"PageTitleWithIconMinimalPreview",
|
||||
"PendingMemberRowWithLongNamePreview",
|
||||
"PinUnlockViewInAppPreview",
|
||||
"PollAnswerViewDisclosedNotSelectedPreview",
|
||||
"PollAnswerViewDisclosedSelectedPreview",
|
||||
"PollAnswerViewEndedSelectedPreview",
|
||||
"PollAnswerViewEndedWinnerNotSelectedPreview",
|
||||
"PollAnswerViewEndedWinnerSelectedPreview",
|
||||
"PollAnswerViewUndisclosedNotSelectedPreview",
|
||||
"PollAnswerViewUndisclosedSelectedPreview",
|
||||
"PollContentViewCreatorEditablePreview",
|
||||
"PollContentViewCreatorEndedPreview",
|
||||
"PollContentViewCreatorPreview",
|
||||
"PollContentViewDisclosedPreview",
|
||||
"PollContentViewEndedPreview",
|
||||
"PollContentViewUndisclosedPreview",
|
||||
"ReadReceiptBottomSheetPreview",
|
||||
"RoomMemberListViewBannedPreview",
|
||||
"SasEmojisPreview",
|
||||
"SecureBackupSetupViewChangePreview",
|
||||
"SelectedUserCannotRemovePreview",
|
||||
"TextComposerAddCaptionPreview",
|
||||
"TextComposerCaptionPreview",
|
||||
"TextComposerEditCaptionPreview",
|
||||
"TextComposerEditNotEncryptedPreview",
|
||||
"TextComposerEditPreview",
|
||||
"TextComposerFormattingNotEncryptedPreview",
|
||||
"TextComposerFormattingPreview",
|
||||
"TextComposerLinkDialogCreateLinkPreview",
|
||||
"TextComposerLinkDialogCreateLinkWithoutTextPreview",
|
||||
"TextComposerLinkDialogEditLinkPreview",
|
||||
"TextComposerReplyPreview",
|
||||
"TextComposerSimpleNotEncryptedPreview",
|
||||
"TextComposerSimplePreview",
|
||||
"TextComposerVoiceNotEncryptedPreview",
|
||||
"TextComposerVoicePreview",
|
||||
"TextFieldDialogWithErrorPreview",
|
||||
"TimelineImageWithCaptionRowPreview",
|
||||
"TimelineItemEventRowForDirectRoomPreview",
|
||||
"TimelineItemEventRowShieldPreview",
|
||||
"TimelineItemEventRowTimestampPreview",
|
||||
"TimelineItemEventRowUtdPreview",
|
||||
"TimelineItemEventRowWithManyReactionsPreview",
|
||||
"TimelineItemEventRowWithRRPreview",
|
||||
"TimelineItemEventRowWithReplyPreview",
|
||||
"TimelineItemGroupedEventsRowContentCollapsePreview",
|
||||
"TimelineItemGroupedEventsRowContentExpandedPreview",
|
||||
"TimelineItemImageViewHideMediaContentPreview",
|
||||
"TimelineItemVideoViewHideMediaContentPreview",
|
||||
"TimelineItemVoiceViewUnifiedPreview",
|
||||
"TimelineVideoWithCaptionRowPreview",
|
||||
"TimelineViewMessageShieldPreview",
|
||||
"UserAvatarColorsPreview",
|
||||
"UserProfileHeaderSectionWithVerificationViolationPreview",
|
||||
"VoiceItemViewPlayPreview",
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `previewNameExceptions is sorted alphabetically`() {
|
||||
assertThat(previewNameExceptions.sorted()).isEqualTo(previewNameExceptions)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `previewNameExceptions only contains existing functions`() {
|
||||
val names = previewNameExceptions.toMutableSet()
|
||||
Konsist
|
||||
.scopeFromProject()
|
||||
.functions()
|
||||
.withAllAnnotationsOf(PreviewsDayNight::class)
|
||||
.withName(previewNameExceptions)
|
||||
.let {
|
||||
it.forEach { function ->
|
||||
names.remove(function.name)
|
||||
}
|
||||
}
|
||||
assertThat(names).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Functions with '@PreviewsDayNight' have correct name`() {
|
||||
Konsist
|
||||
.scopeFromProject()
|
||||
.functions()
|
||||
.withAllAnnotationsOf(PreviewsDayNight::class)
|
||||
.withoutName(
|
||||
"AsyncIndicatorFailurePreview",
|
||||
"AsyncIndicatorLoadingPreview",
|
||||
"BloomInitialsPreview",
|
||||
"BloomPreview",
|
||||
"CallScreenPipViewPreview",
|
||||
"ColorAliasesPreview",
|
||||
"DefaultRoomListTopBarWithIndicatorPreview",
|
||||
"FocusedEventPreview",
|
||||
"GradientFloatingActionButtonCircleShapePreview",
|
||||
"HeaderFooterPageScrollablePreview",
|
||||
"IconsCompoundPreview",
|
||||
"IconsOtherPreview",
|
||||
"MarkdownTextComposerEditPreview",
|
||||
"MatrixBadgeAtomPositivePreview",
|
||||
"MatrixBadgeAtomNeutralPreview",
|
||||
"MatrixBadgeAtomNegativePreview",
|
||||
"MatrixBadgeAtomInfoPreview",
|
||||
"MentionSpanPreview",
|
||||
"MessageComposerViewVoicePreview",
|
||||
"MessagesReactionButtonAddPreview",
|
||||
"MessagesReactionButtonExtraPreview",
|
||||
"MessagesViewWithIdentityChangePreview",
|
||||
"MessagesViewWithTypingPreview",
|
||||
"PageTitleWithIconFullPreview",
|
||||
"PageTitleWithIconMinimalPreview",
|
||||
"PendingMemberRowWithLongNamePreview",
|
||||
"PinUnlockViewInAppPreview",
|
||||
"PollAnswerViewDisclosedNotSelectedPreview",
|
||||
"PollAnswerViewDisclosedSelectedPreview",
|
||||
"PollAnswerViewEndedSelectedPreview",
|
||||
"PollAnswerViewEndedWinnerNotSelectedPreview",
|
||||
"PollAnswerViewEndedWinnerSelectedPreview",
|
||||
"PollAnswerViewUndisclosedNotSelectedPreview",
|
||||
"PollAnswerViewUndisclosedSelectedPreview",
|
||||
"PollContentViewCreatorEditablePreview",
|
||||
"PollContentViewCreatorEndedPreview",
|
||||
"PollContentViewCreatorPreview",
|
||||
"PollContentViewDisclosedPreview",
|
||||
"PollContentViewEndedPreview",
|
||||
"PollContentViewUndisclosedPreview",
|
||||
"ReadReceiptBottomSheetPreview",
|
||||
"RoomMemberListViewBannedPreview",
|
||||
"SasEmojisPreview",
|
||||
"SecureBackupSetupViewChangePreview",
|
||||
"SelectedUserCannotRemovePreview",
|
||||
"TextComposerAddCaptionPreview",
|
||||
"TextComposerCaptionPreview",
|
||||
"TextComposerEditPreview",
|
||||
"TextComposerEditNotEncryptedPreview",
|
||||
"TextComposerEditCaptionPreview",
|
||||
"TextComposerFormattingPreview",
|
||||
"TextComposerFormattingNotEncryptedPreview",
|
||||
"TextComposerLinkDialogCreateLinkPreview",
|
||||
"TextComposerLinkDialogCreateLinkWithoutTextPreview",
|
||||
"TextComposerLinkDialogEditLinkPreview",
|
||||
"TextComposerReplyPreview",
|
||||
"TextComposerReplyNotEncryptedPreview",
|
||||
"TextComposerSimplePreview",
|
||||
"TextComposerSimpleNotEncryptedPreview",
|
||||
"TextComposerVoicePreview",
|
||||
"TextComposerVoiceNotEncryptedPreview",
|
||||
"TextFieldDialogWithBorderPreview",
|
||||
"TextFieldDialogWithErrorPreview",
|
||||
"TimelineImageWithCaptionRowPreview",
|
||||
"TimelineItemEventRowForDirectRoomPreview",
|
||||
"TimelineItemEventRowShieldPreview",
|
||||
"TimelineItemEventRowTimestampPreview",
|
||||
"TimelineItemEventRowUtdPreview",
|
||||
"TimelineItemEventRowWithManyReactionsPreview",
|
||||
"TimelineItemEventRowWithRRPreview",
|
||||
"TimelineItemEventRowWithReplyPreview",
|
||||
"TimelineItemGroupedEventsRowContentCollapsePreview",
|
||||
"TimelineItemGroupedEventsRowContentExpandedPreview",
|
||||
"TimelineItemImageViewHideMediaContentPreview",
|
||||
"TimelineItemVideoViewHideMediaContentPreview",
|
||||
"TimelineItemVoiceViewUnifiedPreview",
|
||||
"TimelineVideoWithCaptionRowPreview",
|
||||
"TimelineViewMessageShieldPreview",
|
||||
"UserAvatarColorsPreview",
|
||||
"UserProfileHeaderSectionWithVerificationViolationPreview",
|
||||
"VoiceItemViewPlayPreview",
|
||||
)
|
||||
.withoutName(previewNameExceptions)
|
||||
.assertTrue(
|
||||
additionalMessage = "Functions for Preview should be named like this: <ViewUnderPreview>Preview. " +
|
||||
"Exception can be added to the test, for multiple Previews of the same view",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:62a7e346397466a3a98bdd2e3dfcad43d65471017d40aa719c00ac2032ff9509
|
||||
size 41231
|
||||
oid sha256:f2adacc3ef3537cc0b81377786d03084f4923a7da1401ab84d03c907569e52d4
|
||||
size 27010
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:abe3d11ea9f0088529da6651526650a1bd2fe34335da31f043cfc8b1fb945597
|
||||
size 49428
|
||||
oid sha256:fb861a1c8c4188b1586384b65c78ed18ca23030066e21c0245606077f89f3c29
|
||||
size 24909
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d079c9142b483482b12e2e55253bb02cf9ff1f37b95d455b1887d54dbfbbb9b0
|
||||
size 40941
|
||||
oid sha256:4a449498d150466a5b3e27001228a39e935be7c637c422955375a18ea6f1fcb2
|
||||
size 26754
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:09c215a680812b95b07501aba343331932c53e8988d13b43e8f3296d84664f63
|
||||
size 49089
|
||||
oid sha256:c4ee431749b5f051b7345ac7f141f88ee8f998f04a2b7743960c5fa27af638e7
|
||||
size 24605
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a7b0dc90002ec59917ed0c64dae785088117c5aca61961c3d71dc7c05d2fa72b
|
||||
size 82238
|
||||
oid sha256:347d73d129f6b49b502371bc0f090e7432bf28f95aade88e37f33b6e91ac44a7
|
||||
size 67519
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:660e96d8c627d8f24bbfbc150a3e2b7e74f6819e7fbaa581c5872f544ed9685e
|
||||
size 46034
|
||||
oid sha256:2cba6e6115836c2274ad829b149d5c85973baf560f8b11e3b7d30023282b8ca8
|
||||
size 34967
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f65131302fab3b0ba710528947376b57230a74731aa6549473998c30f4e07f6e
|
||||
size 40681
|
||||
oid sha256:8a6fa99ca8f18b0fef17abe4d9c8de4a9f34f8d0ae097f55bb6e860f4f7098fb
|
||||
size 29681
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:20e6d5a1c7218304ede990c1906ec628593acbe57a6496b98a2c1fe9c87d1ae3
|
||||
size 105294
|
||||
oid sha256:83807fc3cc614bfc1abb99e062df4dce72aa92626ffb5a24a3fcb357cc5e95c1
|
||||
size 90869
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6041a7693307a30183606e7f8f2b605ba4c9bfb8c7ad5d8122837749813d6e2c
|
||||
size 99928
|
||||
oid sha256:6d5f42641d0566d91fbb55ea5f05d54979eb66c6e0b12c01d84aec1c90c218b8
|
||||
size 85395
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:765619612593a9821c31afb6268259890d9374099f5e26ae9897aca09abf06e9
|
||||
size 82801
|
||||
oid sha256:1eca7e08ad504da3750c622b73450511030a5d0856d7ecede0d23402d39d8855
|
||||
size 68807
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a7b0dc90002ec59917ed0c64dae785088117c5aca61961c3d71dc7c05d2fa72b
|
||||
size 82238
|
||||
oid sha256:347d73d129f6b49b502371bc0f090e7432bf28f95aade88e37f33b6e91ac44a7
|
||||
size 67519
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:51d89e7c0e8c205c230351c4a00e0d560f3f54c6b423ae3b2b801fd6d00343bf
|
||||
size 83509
|
||||
oid sha256:cc132c097ec62633e72292861654fa825f1962eacf8cdcea9553aa0475e2e2fd
|
||||
size 68790
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e7383bea26cbd09cff981e6b4202f7e9bca788094ea482241b45eaefcbf16127
|
||||
size 34927
|
||||
oid sha256:6766c513026e13417d0371f196d69b712a900feeb1f814cda14ba34df188c73a
|
||||
size 23921
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a7b0dc90002ec59917ed0c64dae785088117c5aca61961c3d71dc7c05d2fa72b
|
||||
size 82238
|
||||
oid sha256:347d73d129f6b49b502371bc0f090e7432bf28f95aade88e37f33b6e91ac44a7
|
||||
size 67519
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4385b380efbae924d4a8fece278a61003d40acbd795fef1aa2eab4c5fadbf367
|
||||
size 60691
|
||||
oid sha256:a195f301e7a3b41c3b485cf1e448cde6d12312d0ad06cc48ea5c3cfab2621040
|
||||
size 53715
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7d64fa49cfca399eb073a8efac881aebbfed38726116d3113f9a562245ac9a41
|
||||
size 60495
|
||||
oid sha256:0d26274f91876c8a79846131149b38f544171cdef0396dfaa07c8c3e482aeb61
|
||||
size 53530
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:020d732b7b3a4f43918bee7b9424ffd03fe20074c5823341811a1c700551b2f2
|
||||
size 58734
|
||||
oid sha256:63cb36a2139a260f5589d334dfe012414fa4404d297499d04fb630d206db3b9a
|
||||
size 51750
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:63f8198b96e49f9a29dad22ef2249503d1528aec874599d9daab3f8634d662d7
|
||||
size 99704
|
||||
oid sha256:f67ce8fdbd81b01cee6f1b8f2f1d3f735f14b9afd5c218b3890ee53129b04f39
|
||||
size 85193
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ca1e0f43d447faf770ee7aa4f443f3a310f520c9437cbd5baca9ac7ce36d2aae
|
||||
size 88613
|
||||
oid sha256:3d67192da73b958451d78fdfadbe1d3150fdeed8effd391d98122d4275aac348
|
||||
size 63310
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6be30df0d8f5ba0a57c720bcc16eefc3a5b7dacfc8115a8d47a42edab3e63e82
|
||||
size 53250
|
||||
oid sha256:8ef7257132254bc1a6092da64c643efe1e4ac9f75566f81b05b2afc768248364
|
||||
size 30605
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1c6dc3c0c72d1ea8a980f393d7cfe2c9e0392016c8fc25b901817dd077d8526c
|
||||
size 47265
|
||||
oid sha256:37d62040e49a4ae81ca161b377dab60491984ae1784ed79123658ff4a2df6e18
|
||||
size 24939
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4c93289bc30c3b590360f916a55a86a5cc98028bd2201cb410f329de85ed615c
|
||||
size 111059
|
||||
oid sha256:8600a1be8fd98cf8776e6887b51f5948a28d3f90ade2a189a52a1c1f90655afe
|
||||
size 85983
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7563629f9df4012d7cc35b2805e1ac381ccdb171935b35cad0d1ea60186e3e7d
|
||||
size 105496
|
||||
oid sha256:af5dd69eda38ecc5b461e0f11d2ee1556e8f85b5d26ea28dc90bef41d97f6c9d
|
||||
size 80498
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d7f9c804e0f2b75c84a99081d33d12c46ef2aed62aaf044ec1f957f728f586b1
|
||||
size 88564
|
||||
oid sha256:53552b4c4ebcdfee0da6a64138f7f07080f2d2a0e7db195b327466854b9484b8
|
||||
size 64494
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ca1e0f43d447faf770ee7aa4f443f3a310f520c9437cbd5baca9ac7ce36d2aae
|
||||
size 88613
|
||||
oid sha256:3d67192da73b958451d78fdfadbe1d3150fdeed8effd391d98122d4275aac348
|
||||
size 63310
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3962f724784add8dc33c7327dd866cab76eff0d53abb781b94e974ae68212e0b
|
||||
size 90485
|
||||
oid sha256:3dde30b95371e46ad6d36dfd7c95e2a1e6968517d75b11d16512d5ac53337bb0
|
||||
size 65102
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4850c3002637166016ac75129c0fd4081c540848969b40eb84c4f60a4366c111
|
||||
size 43857
|
||||
oid sha256:6d6454d10e26c7c1ffb14931c38af49ce025a5c0853930c1cddf1f7b2b5e1967
|
||||
size 21562
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ca1e0f43d447faf770ee7aa4f443f3a310f520c9437cbd5baca9ac7ce36d2aae
|
||||
size 88613
|
||||
oid sha256:3d67192da73b958451d78fdfadbe1d3150fdeed8effd391d98122d4275aac348
|
||||
size 63310
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:acde7691ec0ebe690bcb29478963107e2dc0fe6fa54892b3600f50e0cf1db2a3
|
||||
size 69463
|
||||
oid sha256:44c89fe4f267ae04e0ed8f8076e97a90d4a1c55aea47349a16abe8985925677e
|
||||
size 51064
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:38e55635243d77c084053c4feea03dcedabd549aa556f4a622ec2d629bb4f426
|
||||
size 69240
|
||||
oid sha256:146044e82709dfd8262082b31789eed72e9bebc100a7a8f15a7b8d41f5a95ad1
|
||||
size 50833
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1fc45bd4279456b75cadcb8381451f9e6a0e1fadaf4a8f22274c94aa89c7c348
|
||||
size 67496
|
||||
oid sha256:b516e9ad28282895de6671c152aa57e4d8e74345b3745d8b909b39d81e29ebb7
|
||||
size 49075
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8d9f42109a12e450a13d1166f7d7790913e1dd847cd44f00a2c881d825cad9b4
|
||||
size 105496
|
||||
oid sha256:93e35351228d382dd6c890441f09536915b1cd10f2a6592a6f90f52783dc3e46
|
||||
size 80482
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9163c7aaae605af5470506d99eed7ecd19374fa7e719f8b0318e55f1319702a2
|
||||
size 43406
|
||||
oid sha256:c7944e3a428379cd240ef04f81ce82b67f1f42cc4c35c1e986ee37af35b8fcf8
|
||||
size 38434
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:634df4b9fab9797663e11293e5894de9854e918184d227e73e47be047bcb9623
|
||||
size 50335
|
||||
oid sha256:c6ac5f05e753028ee09aaaaf6564113199aff3dada0b42ec534d826ef0aaa96d
|
||||
size 45309
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a971216d0cfc58f65b3589479420c02f2a04010ac27d79ea2434075dcd6b4f7c
|
||||
size 51283
|
||||
oid sha256:3feefaf49170429ce228284a10fc27e3396965a89913ae075fe48ca752029f8d
|
||||
size 46343
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8dcb5fb864d2692e62de1161e3420a3c9e9d7a338110b5e3b7791753e7995b7a
|
||||
size 45863
|
||||
oid sha256:9b47188101c77d3422756c3a904e2c2a567a18cc6b551b77d98cdb465133357d
|
||||
size 40870
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2ee424d3f53556823f1bc19a1bf0d742918dff295962f4dda371459c8607fc15
|
||||
size 34990
|
||||
oid sha256:ae8c3144d1b7e5eb19bf708a48ee4d112d8f95cb9113a2f724e36c63a94bd458
|
||||
size 30060
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6f19a41fa98afa02f2d913c02cfd8cca6e53d6af164ab1c81d98cc75ae877bbc
|
||||
size 36305
|
||||
oid sha256:d3de02fbfc362822fc901339e1c17f832be36dc68a51521e6ceda95b78ab3c05
|
||||
size 31380
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0e527cea5e937b1ab89fcbc0388ab5d81008ceef8e85b25af227f4c8465df268
|
||||
size 31504
|
||||
oid sha256:c863e3132eb572e402067d1c9076b8d91925ce44e50a01975275201f9d1dea5c
|
||||
size 26625
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2604ac285ee6334a46fe348313fe2abe655860bd9b54f237fc92d0f99e38a7be
|
||||
size 29162
|
||||
oid sha256:706915d5e1b6867b044400aa5c11a2da9b69db257343e3fb0c307eb673e60bcd
|
||||
size 27743
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e01e9453b8c72e9a1a55bbb4f14e88836f93d1d9b28af10557aec81eae847d74
|
||||
size 43374
|
||||
oid sha256:22324bf8af409876b3bc6706332761ad66301418f51e05169884b9a6aeda0d2d
|
||||
size 38252
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:90b00f22d1d2d1b22f6260c95ea3fe39851682d258df7012b89c68db2af01601
|
||||
size 50133
|
||||
oid sha256:c1703ac3e99ffa2fd8ea915399bc508a5fc2df66917ae0898986419d731f968a
|
||||
size 44984
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:18d2e25e32b0dc63b72a2c8c32213d2c7d9d499bf568b88addf24709e433ce34
|
||||
size 50997
|
||||
oid sha256:3faabcb1ca9d3cc47f446203ca4037fdf5af2072992c55cd8b20a989393a1990
|
||||
size 45894
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7bed222380fcd708584b52aa098622487025598c870ac3ed68694dcf7e900038
|
||||
size 45755
|
||||
oid sha256:d16cf478ee1c7873706ee1056685b9b7feb0b1a8fdb5dbd31a272bc8536b671e
|
||||
size 40624
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:445c343404fba0ed533ffe07a138ddf74686f67e3e163626963be4e5db17fd7f
|
||||
size 34479
|
||||
oid sha256:4350a822cb6444db068314bd4add32cef9798ff11bd42c3f4e018f11cbe84a1c
|
||||
size 29371
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2e2489ed1cbbd10709456b0a910b8bbb6607732bbf40c4ea30114306875fe999
|
||||
size 36282
|
||||
oid sha256:4d05fb7926b5e09e638a2777e97d51069c993320575a6df41a389a0a362d5c9d
|
||||
size 31210
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5b7b89d817b0d1847828b214a1340fc041e85a44d43fc89dae64e6c56324ac41
|
||||
size 31806
|
||||
oid sha256:ae27e9cae53436ce7b24c2650fafd0c521de8f69efadba4470e4a0474824e2a5
|
||||
size 26793
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1ce6e3a17d5b092b4f292659cea45834207b4956e273b7cecc69c7a07daa8d51
|
||||
size 27255
|
||||
oid sha256:3f2800add49143a4ff174f1d873ba89d9e54376d6474248f49a31cedfbd691fe
|
||||
size 26139
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:cb50ba951a464a7bae750e779f25e2deeda0355a3220fdaf19f60c7c7ed73649
|
||||
size 395064
|
||||
oid sha256:724df787c6c033c40967bfc62de74e71613ef8221bfc18a9859fcc0b5fb9a8ca
|
||||
size 394854
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:503e64a8acb9630e3a6cece0028283407bd54fda2f8ca7bd41c09d12491b48d1
|
||||
size 394757
|
||||
oid sha256:a4b9d7cfa35310566401bc031a2554e49ea71820b3050abc449f91fad3ad892a
|
||||
size 394573
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0ce509e6357edda6b8206eea46a7818386423c96abaddae8ae8e886244a93784
|
||||
size 51168
|
||||
oid sha256:3ccf51958e5757064087f6a589032b7bb25b30827ecfc7864f157a6da689a67c
|
||||
size 51024
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:cb50ba951a464a7bae750e779f25e2deeda0355a3220fdaf19f60c7c7ed73649
|
||||
size 395064
|
||||
oid sha256:724df787c6c033c40967bfc62de74e71613ef8221bfc18a9859fcc0b5fb9a8ca
|
||||
size 394854
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:089a84e094a191836ae654f3214b1b3a590a1b13e0d1972c8ef2ac82813b47eb
|
||||
size 51139
|
||||
oid sha256:15782147de5dc2fda9d77f75694bba22d9998db5c245969baa192e87543387cf
|
||||
size 50991
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:21a0d4fabf74730d3aff1ec23502c18d84cb53899d8cacfefede48ae8fde19e1
|
||||
size 89266
|
||||
oid sha256:9476e888ecf6ac7d374d146a8aa5bfa3ed66b0c4b722d46052b56d177a543733
|
||||
size 89112
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7af7dedec8de4ac367fc870d2c235060b3a57d27b2bb879dc784a3cef3c2a370
|
||||
size 390628
|
||||
oid sha256:36aa7ce68da79c229612156d1870c904a87482b64bc9eb262d2c52c1e813d31d
|
||||
size 390438
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:cb50ba951a464a7bae750e779f25e2deeda0355a3220fdaf19f60c7c7ed73649
|
||||
size 395064
|
||||
oid sha256:724df787c6c033c40967bfc62de74e71613ef8221bfc18a9859fcc0b5fb9a8ca
|
||||
size 394854
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:19066a9752af1dbd27a3403dbc0a85d3b7dfa6b794cfcc314934a91c3e680283
|
||||
size 54925
|
||||
oid sha256:0420404d363d85a0a1215ac8d11f474e148294d6b4818acb65ab6196536c61f9
|
||||
size 55243
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8ead97d45b6ff18a36c589af87a07a3f7e415a1308b7b5cd327de76631063f71
|
||||
size 63435
|
||||
oid sha256:9fced59a8634cae33f42a73cfa1eb2f89ffbec5317622eaf0b1f777ac7eb271b
|
||||
size 63693
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ed176e71df15c6da072badc6db227580ab5b4a4ef41932754fd21c9816c6b16b
|
||||
size 63857
|
||||
oid sha256:e31c7c892a67cf0f36ad2ba20cc87771d59e8165bdfb1bd3fb0adfc53dd79509
|
||||
size 64042
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:70e6b6c940f935d585e210ffa10fa2926f074e4d1ee255e8607345379d2a52f0
|
||||
size 55326
|
||||
oid sha256:2ba3d44f3ab519ae5e2b85a6b93f14c7c38e8ea8cc52baba03e9efef1bcb1145
|
||||
size 54197
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b37969501cdec9c6cbd3604058c420fc95ca239743dcf8f6e88903d3c76c563b
|
||||
size 67268
|
||||
oid sha256:38f24750122c90c71d78000c749a6fae6d440bd3c6b3031cae638c5826f627e9
|
||||
size 66323
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f61cc2b5b7f836d898ef9fa850230ada79303d4caa0f56005a0cb78bc526c184
|
||||
size 66888
|
||||
oid sha256:136f087952cc1f7ef024dfcc84668107e76c2adf8984715ef3c42ebeea68686f
|
||||
size 65837
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:53cd6b98e2f553049f5146e9c73598cd12e7b2199f414473b4258dd37714ce30
|
||||
size 9376
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f978eab0f072af32d574493c0cd89bf8b77749371a64b2dedc0a4f8f4cc29c42
|
||||
size 9069
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a2ea87ef947697fc3ea21c8953149db1c729be5391a4fd65f63b7b04aaf0e8cf
|
||||
size 10465
|
||||
oid sha256:2cd45bad53eed3d47521819d8f8ea750c2bcd7eeccc263a262d7f25093809207
|
||||
size 10658
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8ce07ba72b4542584273ee4004365fe50da4747e4aa06c46ea0d6b192647a1ac
|
||||
size 10204
|
||||
oid sha256:b2c4de97ea960abc79d73ba7ed7eb41202581ab779ad3ce506b9467269bf7e35
|
||||
size 8477
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:bf32ecc91206e24bca38616066b94d6d451f69b1d274dff63e30b73f9b0b5fe2
|
||||
size 16822
|
||||
oid sha256:2e72c1cecdd62de867d300206d4d524f51d8445ed345364d999ec117bea2e0a3
|
||||
size 16926
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0d791d1c3a79e409a43fc5fa3aa368ead2c8aa7167f86e50d477aca6341482f6
|
||||
size 16877
|
||||
oid sha256:4856a029136e5dcc86f21ac66a615d6fd991cbce182e25015ad355b8e0e94fef
|
||||
size 15915
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:bf3f23066d6466f799677dd139d92ff26aeccdc7ce20d1ea4a1fa2eddbc28ecc
|
||||
size 50157
|
||||
oid sha256:baea5001524ac109a9b74ca933b28b596706263aef7a5702f0c014343ad98ce6
|
||||
size 50370
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a78bdd930bc853c4adde525d48f8477e3731cb3e76fc6802ada6350f73e209ce
|
||||
size 331247
|
||||
oid sha256:1f6b9808cd09f5be5c2468bbed0a51a49933b9dd6151b6326b1845e8e4a046c1
|
||||
size 331804
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6eeeacf0a4282d7fd5bc8ef3b67680d72d254c51a8ebd009c9c197180a9a6f0b
|
||||
size 85092
|
||||
oid sha256:5836fb3172f8628993031751a8cebc840167d157776cee7821ac22c84a0db558
|
||||
size 85427
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c242b43c5674dfbe72ad5399ec2a1094fb5d9c8d2ff5d627e49c6f51010fbdf2
|
||||
size 51631
|
||||
oid sha256:a5fed03c9b5212a5b654ae872af03171923c8944ac9fb69ed233e608603439ed
|
||||
size 51806
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9aef1a43f5478f4a8e2715211695d0dffbfdd823d8642244c4411eef06794bee
|
||||
size 63393
|
||||
oid sha256:4bb8a5c67177e2f246e07947d242d0395049409992a108f6cde27fa88ac5d2d9
|
||||
size 63465
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:117ffd9be0ee1c2ce742c1725f935662ddbbf060bf02a3d121f3b331f577eb03
|
||||
size 48007
|
||||
oid sha256:ca73884d2ce866368e6f4a99de05730bac5994e93e8bfcb933d23784407b801a
|
||||
size 48178
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:986daeac63a5185195c9972fb6d6d64cccc1eec53855c755b88d8e4c364acceb
|
||||
size 64484
|
||||
oid sha256:fcde38afea018df257c1ee6f6d17ea969b225ff2acd12f5ed9bcb0414134bcd1
|
||||
size 64571
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4b9bfd23b94519ee37e26e5a6c99e7ec2ac1513cae6cfa8465c71856d1e8afce
|
||||
size 55197
|
||||
oid sha256:cbf93914e100be8c32f20c32067d0b3660f728a46ba8e43e54cbbb3173ead4f0
|
||||
size 55352
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:17fa1c2967b460d47e33dbbbc1f10085941d2861a619c9c4e09d0ad62a6a0e5c
|
||||
size 64217
|
||||
oid sha256:16937747975115844bcaf9fdb0d94ba04059389704066973664b9f3b0baa0587
|
||||
size 64449
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:30ea27e644a6ad86506cebaef9ba6d3558b30945ed57e80ddcbf6cd22896d408
|
||||
size 71126
|
||||
oid sha256:e58cfe72872014d51678373b5500b41201728304c301e6ebd855b2241185e453
|
||||
size 71228
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a8fc7a5830027eb95476df6e378269c1ff37d57c75aac50e3353647d4e84249a
|
||||
size 492060
|
||||
oid sha256:dd648155ac39ce346b8ebd24657bafcaf3668020798b3d50e814e54addc28bdf
|
||||
size 493522
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:949ea4bca530c513ff4e67f4b3b67a69215931ab1fe9bc42b02ed347f02a9bf6
|
||||
size 487167
|
||||
oid sha256:cf409a30502ffe99c991fc5315b34a1f2cb9ef27ffaaa230512752bd80b32d2f
|
||||
size 488703
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8479ff3816b6d08e1b61f9326f525f55f40b6a542baedbfa5f5acbb5c766150e
|
||||
size 70264
|
||||
oid sha256:3bd60ebe776788f2c31f6b3e4e9b2ba9e814a559b78554175648b31e04c19247
|
||||
size 70305
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4d9a77ed75ee8797ff6e76b19d8e873fdcb55b2c361094c875874403887803c1
|
||||
size 85027
|
||||
oid sha256:ea3d7407d2b6f544606c72d2e6dc2b93e3bac1b05a5676b7527dc1a8dff37f7d
|
||||
size 85260
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4767b56c7df7009caa31e905cfa473edfce505b9171214987a0c2be8f76f0558
|
||||
size 73338
|
||||
oid sha256:8877cec4c29fab0b4dff35ab963907c6ba05e222581ecd6b81375c6f13ca55b5
|
||||
size 73416
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:bccefb7d0464ec26c54333804962362923da1cffb834a31b35ccf0460a6420ef
|
||||
size 103533
|
||||
oid sha256:54f9c6207d148648bb231e3956faeacb2f6a29af9532a4a8fe74e4db91875a52
|
||||
size 103802
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:039d1a97453e7c74cc8e2c04ecad719b3dffa1cc023b611e8f6e909fc9d51090
|
||||
size 53106
|
||||
oid sha256:18c3c814f0decaac5608be87344c0fe03509ade2072fca88d774e3858424ff38
|
||||
size 53443
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue