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 2b901f2678
commit 850781444b
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.clickable
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CornerSize import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape 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.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color 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.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import 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.AggregatedReaction
import io.element.android.features.messages.impl.timeline.model.AggregatedReactionProvider import io.element.android.features.messages.impl.timeline.model.AggregatedReactionProvider
import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.ElementTextStyles
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 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.Surface
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.theme.ElementTheme
@Composable @Composable
fun MessagesReactionButton(reaction: AggregatedReaction, modifier: Modifier = Modifier, onClick: () -> Unit) { fun MessagesReactionButton(
val buttonColor = if (reaction.isHighlighted) { onClick: () -> Unit,
content: MessagesReactionsButtonContent,
modifier: Modifier = Modifier,
) {
val buttonColor = if (content.isHighlighted) {
ElementTheme.colors.bgSubtlePrimary ElementTheme.colors.bgSubtlePrimary
} else { } else {
ElementTheme.colors.bgSubtleSecondary ElementTheme.colors.bgSubtleSecondary
} }
val borderColor = if (reaction.isHighlighted) {
val borderColor = if (content.isHighlighted) {
ElementTheme.colors.borderInteractivePrimary ElementTheme.colors.borderInteractivePrimary
} else { } else {
buttonColor buttonColor
} }
Surface( Surface(
modifier = modifier modifier = modifier
.background(Color.Transparent) .background(Color.Transparent)
@ -74,35 +89,91 @@ fun MessagesReactionButton(reaction: AggregatedReaction, modifier: Modifier = Mo
.padding(vertical = 4.dp, horizontal = 10.dp), .padding(vertical = 4.dp, horizontal = 10.dp),
color = buttonColor color = buttonColor
) { ) {
Row( when (content) {
verticalAlignment = Alignment.CenterVertically is MessagesReactionsButtonContent.Icon -> IconContent(imageVector = content.imageVector)
) { is MessagesReactionsButtonContent.Text -> TextContent(text = content.text)
Text( is MessagesReactionsButtonContent.Reaction -> ReactionContent(reaction = content.reaction)
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
)
}
} }
} }
} }
@Preview sealed class MessagesReactionsButtonContent {
@Composable data class Text(val text: String) : MessagesReactionsButtonContent()
internal fun MessagesReactionButtonLightPreview(@PreviewParameter(AggregatedReactionProvider::class) reaction: AggregatedReaction) = data class Icon(val imageVector: ImageVector) : MessagesReactionsButtonContent()
ElementPreviewLight { ContentToPreview(reaction) }
@Preview data class Reaction(val reaction: AggregatedReaction) : MessagesReactionsButtonContent()
@Composable
internal fun MessagesReactionButtonDarkPreview(@PreviewParameter(AggregatedReactionProvider::class) reaction: AggregatedReaction) =
ElementPreviewDark { ContentToPreview(reaction) }
@Composable val isHighlighted get() = this is Reaction && reaction.isHighlighted
private fun ContentToPreview(reaction: AggregatedReaction) {
MessagesReactionButton(reaction, onClick = { })
} }
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 // Reactions
if (event.reactionsState.reactions.isNotEmpty()) { if (event.reactionsState.reactions.isNotEmpty()) {
TimelineItemReactionsView( TimelineItemReactions(
reactionsState = event.reactionsState, reactionsState = event.reactionsState,
mainAxisAlignment = if (event.isMine) FlowMainAxisAlignment.End else FlowMainAxisAlignment.Start, mainAxisAlignment = if (event.isMine) FlowMainAxisAlignment.End else FlowMainAxisAlignment.Start,
onReactionClicked = onReactionClicked, onReactionClicked = onReactionClicked,

View file

@ -16,59 +16,184 @@
package io.element.android.features.messages.impl.timeline.components 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.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.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 androidx.compose.ui.unit.dp
import com.google.accompanist.flowlayout.FlowMainAxisAlignment import com.google.accompanist.flowlayout.FlowMainAxisAlignment
import com.google.accompanist.flowlayout.FlowRow 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.TimelineItemReactions
import io.element.android.features.messages.impl.timeline.model.aTimelineItemReactions import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.ElementPreviewLight 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 @Composable
fun TimelineItemReactionsView( fun TimelineItemReactions(
reactionsState: TimelineItemReactions, reactionsState: TimelineItemReactions,
mainAxisAlignment: FlowMainAxisAlignment, mainAxisAlignment: FlowMainAxisAlignment,
onReactionClicked: (emoji: String) -> Unit, onReactionClicked: (emoji: String) -> Unit,
onMoreReactionsClicked: () -> Unit, onMoreReactionsClicked: () -> Unit,
modifier: Modifier = Modifier, 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( FlowRow(
modifier = modifier, modifier = modifier,
mainAxisSpacing = 4.dp, mainAxisSpacing = 4.dp,
crossAxisSpacing = 4.dp, crossAxisSpacing = 4.dp,
mainAxisAlignment = mainAxisAlignment, mainAxisAlignment = mainAxisAlignment,
) { ) {
reactionsState.reactions.forEach { reaction -> reactions.forEach { reaction ->
MessagesReactionButton( MessagesReactionButton(
reaction = reaction, content = MessagesReactionsButtonContent.Reaction(reaction = reaction),
onClick = { onReactionClicked(reaction.key) } onClick = { onReactionClick(reaction.key) }
) )
} }
MessagesMoreReactionsButton( when (expandableState) {
onClick = onMoreReactionsClicked 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 @Composable
internal fun TimelineItemReactionsViewLightPreview() = fun TimelineItemReactionsViewPreview() = ElementPreview {
ElementPreviewLight { ContentToPreview() } ContentToPreview(
reactions = aTimelineItemReactions(count = 1).reactions,
@Preview expandableState = ExpandableState.None,
@Composable
internal fun TimelineItemReactionsViewDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
TimelineItemReactionsView(
reactionsState = aTimelineItemReactions(),
mainAxisAlignment = FlowMainAxisAlignment.Center,
onReactionClicked = {},
onMoreReactionsClicked = {},
) )
} }
@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="one">"%1$d room change"</item>
<item quantity="other">"%1$d room changes"</item> <item quantity="other">"%1$d room changes"</item>
</plurals> </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">"Camera"</string>
<string name="screen_room_attachment_source_camera_photo">"Take photo"</string> <string name="screen_room_attachment_source_camera_photo">"Take photo"</string>
<string name="screen_room_attachment_source_camera_video">"Record a video"</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 @Composable
fun Dp.toSp(): TextUnit = with(LocalDensity.current) { toSp() } 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. * 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 version https://git-lfs.github.com/spec/v1
oid sha256:6ac13d38a9aaf4341ff8845141dcbc90033f7105750e0ea212bb7b4387dd72ad oid sha256:b8ca3e1c8ce3c9476cdc7db46aebc8b6970486ff01e05b82c162256373c8957f
size 141063 size 141055

View file

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

View file

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

View file

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

View file

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

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:5896cb2b9847d2f67bc6d7c39a2499286c59945b9e2c4961e191e547739f41e7 oid sha256:d50e5ac4433e4a44091623f1813373884007506002cc0c657ca7231a6e309462
size 123936 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 version https://git-lfs.github.com/spec/v1
oid sha256:0b5e8b131784aa74261ebeef065f027c8ed4ab6188b390bc9849d3d4bac747e2 oid sha256:6730052131679024056a86514c28639bd975277e46aefefa92b5b31a53a4037d
size 51648 size 51674

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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