Collapse long lists of message reactions (#806)

---------

Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
jonnyandrew 2023-07-07 13:19:11 +00:00 committed by GitHub
parent 54c7e8bb57
commit d8fcfc5844
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
72 changed files with 387 additions and 254 deletions

View file

@ -1,87 +0,0 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.messages.impl.timeline.components
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AddReaction
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.theme.ElementTheme
@Composable
fun MessagesMoreReactionsButton(modifier: Modifier = Modifier, onClick: () -> Unit) {
val buttonColor = ElementTheme.colors.bgSubtleSecondary
Surface(
modifier = modifier
.background(Color.Transparent)
// Outer border, same colour as background
.border(
BorderStroke(2.dp, MaterialTheme.colorScheme.background),
shape = RoundedCornerShape(corner = CornerSize(14.dp))
)
.padding(vertical = 2.dp, horizontal = 2.dp)
// Clip click indicator inside the outer border
.clip(RoundedCornerShape(corner = CornerSize(12.dp)))
.clickable(onClick = onClick)
.background(buttonColor, RoundedCornerShape(corner = CornerSize(12.dp)))
.padding(vertical = 4.dp, horizontal = 10.dp),
color = buttonColor
) {
Icon(
imageVector = Icons.Outlined.AddReaction,
contentDescription = "Add emoji",
tint = MaterialTheme.colorScheme.secondary,
modifier = Modifier
// Same size as the line height of reaction emoji text
.size(with(LocalDensity.current) { 20.sp.toDp() })
)
}
}
@Preview
@Composable
internal fun MessagesMoreReactionsButtonLightPreview() =
ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun MessagesMoreReactionsButtonDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
MessagesMoreReactionsButton(onClick = {})
}

View file

@ -22,40 +22,55 @@ import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AddReaction
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.features.messages.impl.R
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
import io.element.android.features.messages.impl.timeline.model.AggregatedReactionProvider
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.ElementTextStyles
import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
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.Surface
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
@Composable
fun MessagesReactionButton(reaction: AggregatedReaction, modifier: Modifier = Modifier, onClick: () -> Unit) {
val buttonColor = if (reaction.isHighlighted) {
fun MessagesReactionButton(
onClick: () -> Unit,
content: MessagesReactionsButtonContent,
modifier: Modifier = Modifier,
) {
val buttonColor = if (content.isHighlighted) {
ElementTheme.colors.bgSubtlePrimary
} else {
ElementTheme.colors.bgSubtleSecondary
}
val borderColor = if (reaction.isHighlighted) {
val borderColor = if (content.isHighlighted) {
ElementTheme.colors.borderInteractivePrimary
} else {
buttonColor
}
Surface(
modifier = modifier
.background(Color.Transparent)
@ -74,35 +89,91 @@ fun MessagesReactionButton(reaction: AggregatedReaction, modifier: Modifier = Mo
.padding(vertical = 4.dp, horizontal = 10.dp),
color = buttonColor
) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = reaction.key, fontSize = 15.sp, lineHeight = 20.sp
)
if (reaction.count > 1) {
Spacer(modifier = Modifier.width(4.dp))
Text(
text = reaction.count.toString(),
color = if (reaction.isHighlighted) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.secondary,
fontSize = 14.sp
)
}
when (content) {
is MessagesReactionsButtonContent.Icon -> IconContent(imageVector = content.imageVector)
is MessagesReactionsButtonContent.Text -> TextContent(text = content.text)
is MessagesReactionsButtonContent.Reaction -> ReactionContent(reaction = content.reaction)
}
}
}
@Preview
@Composable
internal fun MessagesReactionButtonLightPreview(@PreviewParameter(AggregatedReactionProvider::class) reaction: AggregatedReaction) =
ElementPreviewLight { ContentToPreview(reaction) }
sealed class MessagesReactionsButtonContent {
data class Text(val text: String) : MessagesReactionsButtonContent()
data class Icon(val imageVector: ImageVector) : MessagesReactionsButtonContent()
@Preview
@Composable
internal fun MessagesReactionButtonDarkPreview(@PreviewParameter(AggregatedReactionProvider::class) reaction: AggregatedReaction) =
ElementPreviewDark { ContentToPreview(reaction) }
data class Reaction(val reaction: AggregatedReaction) : MessagesReactionsButtonContent()
@Composable
private fun ContentToPreview(reaction: AggregatedReaction) {
MessagesReactionButton(reaction, onClick = { })
val isHighlighted get() = this is Reaction && reaction.isHighlighted
}
private val reactionEmojiLineHeight = 20.sp
@Composable
private fun TextContent(
text: String,
modifier: Modifier = Modifier,
) = Text(
modifier = modifier
.height(reactionEmojiLineHeight.toDp()),
text = text,
style = ElementTextStyles.Regular.bodyMD
)
@Composable
private fun IconContent(
imageVector: ImageVector,
modifier: Modifier = Modifier
) = Icon(
imageVector = imageVector,
contentDescription = stringResource(id = R.string.screen_room_timeline_add_reaction),
tint = MaterialTheme.colorScheme.secondary,
modifier = modifier
.size(reactionEmojiLineHeight.toDp())
)
@Composable
fun ReactionContent(
reaction: AggregatedReaction,
modifier: Modifier = Modifier,
) = Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier,
) {
Text(
text = reaction.key,
fontSize = 15.sp, lineHeight = reactionEmojiLineHeight
)
if (reaction.count > 1) {
Spacer(modifier = Modifier.width(4.dp))
Text(
text = reaction.count.toString(),
color = if (reaction.isHighlighted) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.secondary,
fontSize = 14.sp
)
}
}
@DayNightPreviews
@Composable
internal fun MessagesReactionButtonPreview(@PreviewParameter(AggregatedReactionProvider::class) reaction: AggregatedReaction) = ElementPreview {
MessagesReactionButton(
content = MessagesReactionsButtonContent.Reaction(reaction),
onClick = {}
)
}
@DayNightPreviews
@Composable
internal fun MessagesReactionExtraButtonsPreview() = ElementPreview {
Row {
MessagesReactionButton(
content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction),
onClick = {}
)
MessagesReactionButton(
content = MessagesReactionsButtonContent.Text("12 more"),
onClick = {}
)
}
}

View file

@ -249,7 +249,7 @@ private fun TimelineItemEventRowContent(
// Reactions
if (event.reactionsState.reactions.isNotEmpty()) {
TimelineItemReactionsView(
TimelineItemReactions(
reactionsState = event.reactionsState,
mainAxisAlignment = if (event.isMine) FlowMainAxisAlignment.End else FlowMainAxisAlignment.Start,
onReactionClicked = onReactionClicked,

View file

@ -16,59 +16,184 @@
package io.element.android.features.messages.impl.timeline.components
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AddReaction
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
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.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.google.accompanist.flowlayout.FlowMainAxisAlignment
import com.google.accompanist.flowlayout.FlowRow
import io.element.android.features.messages.impl.R
import io.element.android.features.messages.impl.timeline.aTimelineItemReactions
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions
import io.element.android.features.messages.impl.timeline.model.aTimelineItemReactions
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toPersistentList
/**
* The maximum number of items that can be displayed before some items will be hidden
*
* TODO The threshold should be based on the number of rows, rather than items.
* Once items would spill onto a third row, they should be hidden.
* Note this could be particularly worthwhile to handle reactions that are
* longer than a single character (as annotation keys are free text).
*/
private const val COLLAPSE_ITEMS_THRESHOLD = 8
@Composable
fun TimelineItemReactionsView(
fun TimelineItemReactions(
reactionsState: TimelineItemReactions,
mainAxisAlignment: FlowMainAxisAlignment,
onReactionClicked: (emoji: String) -> Unit,
onMoreReactionsClicked: () -> Unit,
modifier: Modifier = Modifier,
) {
var expanded: Boolean by rememberSaveable { mutableStateOf(false) }
val reactions by remember(reactionsState, expanded) {
derivedStateOf {
val numToDisplay = if (expanded) {
reactionsState.reactions.count()
} else {
COLLAPSE_ITEMS_THRESHOLD
}
reactionsState.reactions.take(numToDisplay).toPersistentList()
}
}
val expandableState by remember {
derivedStateOf {
if (expanded) {
ExpandableState.Expanded
} else {
val hiddenItems = reactionsState.reactions.count() - reactions.count()
if (hiddenItems > 0) {
ExpandableState.Collapsed(hidden = hiddenItems)
} else {
ExpandableState.None
}
}
}
}
TimelineItemReactionsView(
modifier = modifier,
reactions = reactions,
expandableState = expandableState,
mainAxisAlignment = mainAxisAlignment,
onReactionClick = onReactionClicked,
onMoreReactionsClick = onMoreReactionsClicked,
onExpandClick = { expanded = true },
onCollapseClick = { expanded = false }
)
}
private sealed class ExpandableState {
object None: ExpandableState()
data class Collapsed(val hidden: Int): ExpandableState()
object Expanded : ExpandableState()
}
@Composable
private fun TimelineItemReactionsView(
reactions: ImmutableList<AggregatedReaction>,
expandableState: ExpandableState,
mainAxisAlignment: FlowMainAxisAlignment,
onReactionClick: (emoji: String) -> Unit,
onMoreReactionsClick: () -> Unit,
onExpandClick: () -> Unit,
onCollapseClick: () -> Unit,
modifier: Modifier = Modifier
) =
FlowRow(
modifier = modifier,
mainAxisSpacing = 4.dp,
crossAxisSpacing = 4.dp,
mainAxisAlignment = mainAxisAlignment,
) {
reactionsState.reactions.forEach { reaction ->
reactions.forEach { reaction ->
MessagesReactionButton(
reaction = reaction,
onClick = { onReactionClicked(reaction.key) }
content = MessagesReactionsButtonContent.Reaction(reaction = reaction),
onClick = { onReactionClick(reaction.key) }
)
}
MessagesMoreReactionsButton(
onClick = onMoreReactionsClicked
when (expandableState) {
ExpandableState.Expanded ->
MessagesReactionButton(
content = MessagesReactionsButtonContent.Text(
text = stringResource(id = R.string.screen_room_timeline_less_reactions)
),
onClick = onCollapseClick,
)
is ExpandableState.Collapsed -> {
val hidden = expandableState.hidden
MessagesReactionButton(
content = MessagesReactionsButtonContent.Text(
text = pluralStringResource(id = R.plurals.screen_room_timeline_more_reactions, hidden, hidden)
),
onClick = onExpandClick,
)
}
ExpandableState.None -> {
// No expand or collapse action available
}
}
MessagesReactionButton(
content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction),
onClick = onMoreReactionsClick
)
}
}
@Preview
@DayNightPreviews
@Composable
internal fun TimelineItemReactionsViewLightPreview() =
ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun TimelineItemReactionsViewDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
TimelineItemReactionsView(
reactionsState = aTimelineItemReactions(),
mainAxisAlignment = FlowMainAxisAlignment.Center,
onReactionClicked = {},
onMoreReactionsClicked = {},
fun TimelineItemReactionsViewPreview() = ElementPreview {
ContentToPreview(
reactions = aTimelineItemReactions(count = 1).reactions,
expandableState = ExpandableState.None,
)
}
@DayNightPreviews
@Composable
fun TimelineItemReactionsViewCollapsedPreview() = ElementPreview {
ContentToPreview(
reactions = aTimelineItemReactions(count = 3).reactions,
expandableState = ExpandableState.Collapsed(hidden = 7),
)
}
@DayNightPreviews
@Composable
fun TimelineItemReactionsViewExpandedPreview() = ElementPreview {
ContentToPreview(
reactions = aTimelineItemReactions(count = 10).reactions,
expandableState = ExpandableState.Expanded,
)
}
@Composable
private fun ContentToPreview(
reactions: ImmutableList<AggregatedReaction>,
expandableState: ExpandableState
) {
TimelineItemReactionsView(
reactions = reactions,
expandableState = expandableState,
mainAxisAlignment = FlowMainAxisAlignment.Center,
onReactionClick = {},
onMoreReactionsClick = {},
onExpandClick = {},
onCollapseClick = {}
)
}

View file

@ -4,6 +4,11 @@
<item quantity="one">"%1$d room change"</item>
<item quantity="other">"%1$d room changes"</item>
</plurals>
<string name="screen_room_timeline_less_reactions">"Show less"</string>
<plurals name="screen_room_timeline_more_reactions">
<item quantity="other">"%1$d more"</item>
</plurals>
<string name="screen_room_timeline_add_reaction">"Add emoji"</string>
<string name="screen_room_attachment_source_camera">"Camera"</string>
<string name="screen_room_attachment_source_camera_photo">"Take photo"</string>
<string name="screen_room_attachment_source_camera_video">"Record a video"</string>

View file

@ -28,6 +28,13 @@ import androidx.compose.ui.unit.TextUnit
@Composable
fun Dp.toSp(): TextUnit = with(LocalDensity.current) { toSp() }
/**
* Convert Sp to Dp, regarding current density.
* Can be used for instance to use Sp unit for size.
*/
@Composable
fun TextUnit.toDp(): Dp = with(LocalDensity.current) { toDp() }
/**
* Convert Px value to Dp, regarding current density.
*/

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:211784ef45a8d357c2a9e9d8b4a16ea10be71d14613ab1e32119ed8fa584831a
size 6365

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8e2085deec0b6096437c82204e8da30e143f3c669c9759dbf34c26d63bbf4442
size 7232

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c01fa8074194cf65443f2f321d3ce6b313b7ade8aa7f41155acbfd2a1728e210
size 7147

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:47ba3b562872e6078e48bd3168c3da54515ea7196fc5310e520a1876972bad16
size 8020

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b024b20585c98eddb6032795e2a434bf675f7254d1f3a649ccccbe4fb2351c8f
size 7844

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cb3b2ccab39aa4cb7afde21d8a9b2c0d18f54d9f397e7020413281b3bfebaa2f
size 7634

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6ac13d38a9aaf4341ff8845141dcbc90033f7105750e0ea212bb7b4387dd72ad
size 141063
oid sha256:b8ca3e1c8ce3c9476cdc7db46aebc8b6970486ff01e05b82c162256373c8957f
size 141055

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:651535d566f1bf250b0856d3fd6d3726aa2f6714d44217701e08c805d3771347
size 145406
oid sha256:2bf89d0228a9771360eaad8a0bc514731153a351551df427cf7f7e44b886edfe
size 145398

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7a1c0e5249236abdde5de337f2fe8c8f58bb2ad8bf31fe2f4214dbe35f327915
size 129834
oid sha256:82b4fb72cd3517b4eedeba3172cc428ba67199df816c686d998027ed021c1bb0
size 84164

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1276ef584823b28330ee3facc4cbebdaf9bc6cb02f59af2c023a740677940f02
size 133065
oid sha256:7c95c7df9616dca071528f4185e63485e7e738e51e595d828f94b08205c6e33d
size 87695

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:47760aad8040e34898f8d80d207a6fdb6255255f72bcc2f8cb99801bedebabd4
size 119160
oid sha256:02f2eb6e37f1022aa13cbcdfc34e7955b99ee4c3d6b35668b548afc94d33ae31
size 119137

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5896cb2b9847d2f67bc6d7c39a2499286c59945b9e2c4961e191e547739f41e7
size 123936
oid sha256:d50e5ac4433e4a44091623f1813373884007506002cc0c657ca7231a6e309462
size 123938

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6cc714b45529c63b455cd79fbec2057edf17eddc7352f01ed299115766894a33
size 13729

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f1a633da75e5518aacf9b9be08afc27e0627cdebb86544f2de5b698103f1703c
size 13533

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a751a46c2aff2337d941442709910d442dd48496f8886e8dc510df17feea062c
size 27180

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:71f688355e6a18472424c0958fe1c3baea7e53b4cc4d2d54806434174ff67e71
size 27002

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:82550de45d39c52f34fca45f84b6e9eff53dfef6d2e9622dcaf49c707927d665
size 7873

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fc39041ac035aa372640ca7b2e9af254564aa10ce7a7f4c55fbc4bca9f6dc6d9
size 7844

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0b5e8b131784aa74261ebeef065f027c8ed4ab6188b390bc9849d3d4bac747e2
size 51648
oid sha256:6730052131679024056a86514c28639bd975277e46aefefa92b5b31a53a4037d
size 51674

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8d26f0735eda30013ad31475347978d778e231e6b2ece7a3528a0b9a2f3743c5
size 63185
oid sha256:a9843fe657398792239c1058a0814e639072ebd6673bb2ecf8ff3ae309add40c
size 63126

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9eba210c27876f999977e75874b4ed2d89f591e16734133ae140f5e82e7fe879
size 49724
oid sha256:43ee66a5472f660e9d0f1dfa3876406d5e05723b0c86f0adeb0e5f77bf0c1d27
size 49740

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e88421b82ca846b94a0eba7a00d424dd077586ebdc28c5364cdb07e4f2d6e2ee
size 66031
oid sha256:b5dfdfdd9b5ffeee9d9af468c84144c85fcf16f145bfd9700825cf5bcb324cc2
size 65969

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3e86e8491e170371cb229f2e58a1fda5efe5ac3dc3b87b4f928bfb74d9fd3ded
size 56368
oid sha256:cc84c10136a833f1f418de126535cccad09e6ea75f31ca7dd46f85e5eb1b272b
size 56377

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:33e25642dd17c10bb39f576f33ebd673619b76c111fd4b5834cd51644cb57b8b
size 229779
oid sha256:14a299ad0ed6520f347d53206964d91691ce9e281001c93e3b5393d0a169d753
size 229787

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7ae3b529f45c9a5c739193eee0dfd9c4ad73310deae8eb50e1b0f57205de84a6
size 230761
oid sha256:9f2c3a472d0aad53f531852343b572e0ad980fbd658a7b84779bfceb635e8569
size 230768

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:eece03c2cf471a428d6f383ab3ba2e763a333f10bedf6b4d7a86efd35ffb960d
size 70996
oid sha256:57cce1248b91028d53219b23d52c751a8ab9e85c475c11e60bb91f02b774c854
size 70948

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:df108cf01c6ef1a061d7f84bdb283b9a7cea45ed3a6eea703c110a6981435733
size 85260
oid sha256:131801a6f63c5f0738ec5303a631161ef651da6a714ce60eec9fc693d1fbccae
size 85282

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b0efa0cbf46ffc038bd7ff522b0f81587438f564dd9a7ed7b5b995ea9b3fda31
size 174560
oid sha256:dc88c24d4f5180ae3cfc1d936d901f3e31feaba8fabfcb8a67477f46ab10912e
size 174514

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5ee5b3a256c3ebac9accb7a4361fd1e29c88e21d4c8b10be293bff368d3dd966
size 165233
oid sha256:7b8110ca00a0889ac02c1ec820f7e1750d57f84ec42b033d662898e5ba06bd79
size 165223

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:538df93c96ec8d52f370607cb460c0c507cbf1d716cd8150979bc555e7c4dd92
size 53061
oid sha256:1075736c7eebc685290898a39d844cbd63984e031360a913904ace7ea8bd9a0e
size 53065

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:09f899772bfd8fb4fcff729ba2b4236a842b6aba27a7c7efcbe15f60379ac067
size 65090
oid sha256:192ba37d34e3634559d25fcaf5c214fd53784bb304a1396a3e7704b498eeae44
size 65018

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a9cf9fd968fe92c8e5f4402182689d400a554e423905a8d69a065e8ecf7461ba
size 53294
oid sha256:30fbb6bca73b70b5466a95c1e99e35a9b722e768e192cc00edf16782881d0d69
size 53302

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3565ffe08fd9472a2a3298e0d1c65b2066bbf318f7c3564d3ca8022a22815750
size 65710
oid sha256:ae3cf36193474a4f2c057df5aa8d6caaca9cecf13abcd92998d8d6b46a054dc3
size 65739

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:85c2a8b9910c6f751902a40702fe605b5b50ee22b9d61d258500c0c9dfed07dd
size 51179
oid sha256:ce73d9459b8d41a9a47de3eef6627110990e3d077d81e240e227c7ad70d8f90e
size 51200

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e07e00d3940bbc0f0cee49c76f82a1ad638616ecbab98599fb94c871628a8557
size 68782
oid sha256:2d437b4eb2aec598bdd2ef3c20ce186e18d61697fc6c4c53c8e6ed264dbdd377
size 68813

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2858fb6aff4161239713634efe4040878a3e836173386c80675b962c9f87131f
size 58413
oid sha256:0317b3b8a387f168037a196e685d27fd89515048bf6f1cb28820bd4bcff5fb49
size 58425

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9dff8dd2a36d8cb5521e7b7f4e395f87038dbd1c4886e92c94be122d18248f17
size 230285
oid sha256:65f1933991e74965f8f0848c88e54305d84dea3820ba75722e831ee5b9b44e75
size 230252

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:873e746f40b06d4cfe2af496dd4bf2d6ad761412ae3c3e72b6184282b2225346
size 231253
oid sha256:df7c258ab50b5baeccf22434b343226d057b9127ce7a4a74489883e6aca98e94
size 231221

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4d49e59b6ed1ccd2fbd22ed25ffd31619fda33b709a1af8546ceed2328fad6f0
size 73767
oid sha256:c80f71a5db4355418c4ade1e46536ff44e0f97bb0177a658e5bec0da367c13e6
size 73764

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:db4b63cae9b106c125e53296875d3032ea1cd2a455c39c39bb8bd3bd514ab2ac
size 89827
oid sha256:50ee9e9ca2bf1eb8d0a01210a919eb8370a8125ff6d63c9934861296d781148a
size 89841

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a1800f8b515a557d93f25ef1c2dae4f11b29de95f4ee9af649bb40bad1014966
size 364477
oid sha256:d077c1ca9d9f373b14e1cd657b8d112dd255b9956176cdc252155a53d433fa99
size 364460

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dc59dde6b5b1f0b894de2dc91516e2e9ae35d9b1d0d523b6a488b9366c57289d
size 323483
oid sha256:eeb039373b3898a42bbfd89c7bcb61b152cb45498f50994297a1f99ac13ae60b
size 323473

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:eaa7b8ef747034fd29d383902436d491c4544e5a14f455fa29697120bbbcb204
size 54917
oid sha256:773ce37927e091ed7eb458e2c1260e08c947bb2d737f5977b3c1443962938a14
size 54940

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:88788496a3be3ef0a464377869ef1ca2e7abe4eb8b96c5d69b819afed0470683
size 67671
oid sha256:af43a77aadb7fdf6e8fa2472836ecae4d1a764b2995a1dabff2f6c2ba9e35ccb
size 67704

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:341ead0eb1a09351740738bd540bcf8c6c96862c3370b95b764617335e5b4f22
size 52510
oid sha256:cd827d099fae0d46cfed9f9beaa8c8ee4231d79b86e3d9e5a0db435a7a89adab
size 52537

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:96b7a3cd0f79994d4195e4abe1ee07381f98c6c5c51760226a995efc09617a03
size 54039
oid sha256:da41884d88decec8e9fa742bd901916a74d6c5b321abc7f8bc6f0d03ec923409
size 54062

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e8302d3b48c733500927ba7e5878f001bd7dced72ebd8386142931d557757028
size 52706
oid sha256:497a831673a4837a548d3cd5a83f8c2d5ff379c93b8080994c5c18470fa730a7
size 52731

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2c6dbd33edb880fea91d290a24260c17573d4d36140f12a472c28c77441c8684
size 55697
oid sha256:beb5bf49b50d78bfa607190fee545d5664be56becf9cbb5ca930b0d3779c4c52
size 55667

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:159b54d8c45229e37a3ca9748ab52a6ef448ad93aed209b4e770e94ff5196373
size 50706
oid sha256:204246b9d5913925f9d3899a74c97d602aeb81b262797f4fbef1a3c20b4cbee1
size 50665

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f1ffe365eedfe6b054a268fb3a6a10599df9152d5008359ea95ab7b90b13a33e
size 54389
oid sha256:d8c430b4caaafe56526e2436238675156948032d0877d72330f2cac03d2d2568
size 54405

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2ddcc0519e33b2f6a93d873d2e6aee000ca5a38d2dbd2122869e0befa0c78c7d
size 55949
oid sha256:cec94dbc2f0515af4bf3c79f9376e37a2f2b6e7b1227af36a20963ac528b766a
size 55964

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:96fdf93ee3e67f4f575928a842ce309c4675d361526b9b2714ef2c3fe7167ff4
size 54675
oid sha256:21093f6d5209487fbc6218ad37a1daf62c946f38cefe8c1e82da8e9223335da7
size 54696

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5694004acfac3f1c573dd7929897e01ea9b7e7927cc209e6f4b9bd5568c5ed96
size 57714
oid sha256:7a4c2a1d91f1ba87019ee9ddc254184e6d241284ff15ebf3cca2ad2eeddc3a78
size 57666

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d2ff9337e57cecce1404c44b7b9eb7d6fce65a22fb94326429bd25041e59a588
size 52250
oid sha256:2f117563be9539efa9cc3790a3ca0ff8ff7718184a9a00eb2c540cfcb36f5d58
size 52251