Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
263 changes: 154 additions & 109 deletions app/src/main/java/friendly/android/FeedCard.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
Expand All @@ -23,13 +23,14 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ElevatedSuggestionChip
import androidx.compose.material3.CardDefaults.elevatedCardElevation
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilledTonalIconButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.SuggestionChip
import androidx.compose.material3.SuggestionChipDefaults
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
Expand Down Expand Up @@ -64,8 +65,9 @@ fun FeedCard(
modifier: Modifier = Modifier,
) {
Box(modifier = modifier.fillMaxSize()) {
OutlinedCard(
ElevatedCard(
modifier = Modifier.fillMaxSize(),
elevation = elevatedCardElevation(),
) {
FeedCardContent(
entry = entry,
Expand All @@ -91,41 +93,31 @@ private fun FeedCardContent(
.verticalScroll(rememberScrollState())
.fillMaxSize(),
) {
AvatarWithInterests(
AvatarWithOverlay(
nickname = entry.nickname,
userId = entry.id,
avatarUri = entry.avatarUri,
interests = entry.interests,
isExtendedNetwork = entry.isExtendedNetwork,
isRequest = entry.isRequest,
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f),
)

Spacer(modifier = Modifier.height(8.dp))

Column(
modifier = Modifier
.padding(horizontal = 12.dp)
.fillMaxSize(),
Surface(
color = MaterialTheme.colorScheme.surfaceContainerLow,
) {
Text(
text = entry.nickname.string,
style = MaterialTheme.typography.headlineMedium,
fontWeight = FontWeight.SemiBold,
modifier = Modifier.fillMaxWidth(),
)

Spacer(Modifier.height(8.dp))

ExpandableDescription(
description = entry.description,
collapsedMaxLine = 7,
expandText = stringResource(R.string.expand),
modifier = Modifier
.fillMaxWidth(),
)
Column {
ExpandableDescription(
description = entry.description,
collapsedMaxLine = 7,
expandText = stringResource(R.string.expand),
modifier = Modifier.fillMaxWidth(),
interests = entry.interests,
)

Spacer(Modifier.height(96.dp))
Spacer(Modifier.height(96.dp))
}
}
}

Expand All @@ -137,7 +129,9 @@ private fun FeedCardContent(
) {
FilledTonalIconButton(
colors = IconButtonDefaults.iconButtonColors(
containerColor = MaterialTheme.colorScheme.errorContainer,
containerColor =
MaterialTheme.colorScheme.surfaceContainerHighest,
contentColor = MaterialTheme.colorScheme.onSurface,
),
onClick = { dislike(entry) },
modifier = Modifier
Expand All @@ -157,6 +151,10 @@ private fun FeedCardContent(
Spacer(Modifier.weight(1f))

FilledTonalIconButton(
colors = IconButtonDefaults.iconButtonColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
),
onClick = { like(entry) },
modifier = Modifier.size(
IconButtonDefaults.mediumContainerSize(
Expand All @@ -172,32 +170,6 @@ private fun FeedCardContent(
)
}
}

Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
modifier = Modifier
.padding(8.dp)
.align(Alignment.TopStart),
) {
if (entry.isRequest) {
Surface(modifier = Modifier.clip(RoundedCornerShape(16.dp))) {
Text(
text = stringResource(R.string.friend_request),
modifier = Modifier
.padding(horizontal = 12.dp, vertical = 8.dp),
)
}
}
if (entry.isExtendedNetwork) {
Surface(modifier = Modifier.clip(RoundedCornerShape(16.dp))) {
Text(
text = stringResource(R.string.extended_network),
modifier = Modifier
.padding(horizontal = 12.dp, vertical = 8.dp),
)
}
}
}
}
}

Expand All @@ -206,68 +178,84 @@ fun ExpandableDescription(
description: UserDescription,
collapsedMaxLine: Int = 7,
expandText: String,
interests: List<Interest>,
modifier: Modifier = Modifier,
) {
val text = description.string
var isExpanded by remember { mutableStateOf(false) }
var isClickable by remember { mutableStateOf(false) }
var lastCharacterIndex by remember { mutableStateOf(0) }

Column(
Surface(
color = MaterialTheme.colorScheme.surfaceContainer,
modifier = modifier
.padding(horizontal = 8.dp)
.clickable(
onClick = { isExpanded = true },
interactionSource = remember { MutableInteractionSource() },
indication = null,
),
)
.clip(MaterialTheme.shapes.medium),
) {
val annotatedText = buildAnnotatedString {
if (isClickable) {
if (isExpanded) {
append(text)
} else {
val adjustText = text
.take(lastCharacterIndex)
.dropLast(expandText.length)
.dropLastWhile { it.isWhitespace() || it == '.' }
Column(Modifier.padding(vertical = 16.dp)) {
Interests(interests)

append(adjustText)
Spacer(Modifier.height(8.dp))

val annotatedText = buildAnnotatedString {
if (isClickable) {
if (isExpanded) {
append(text)
} else {
val adjustText = text
.take(lastCharacterIndex)
.dropLast(expandText.length)
.dropLastWhile { it.isWhitespace() || it == '.' }

append(adjustText)
}
} else {
append(text)
}
} else {
append(text)
}
}

Text(
text = annotatedText,
maxLines = if (isExpanded) Int.MAX_VALUE else collapsedMaxLine,
onTextLayout = { textLayoutResult ->
if (!isExpanded && textLayoutResult.hasVisualOverflow) {
isClickable = true
lastCharacterIndex = textLayoutResult
.getLineEnd(collapsedMaxLine - 1)
}
},
modifier = Modifier.animateContentSize(),
)
if (isClickable && !isExpanded) {
Text(
text = expandText,
fontWeight = FontWeight.Black,
textAlign = TextAlign.End,
text = annotatedText,
maxLines = if (isExpanded) Int.MAX_VALUE else collapsedMaxLine,
onTextLayout = { textLayoutResult ->
if (!isExpanded && textLayoutResult.hasVisualOverflow) {
isClickable = true
lastCharacterIndex = textLayoutResult
.getLineEnd(collapsedMaxLine - 1)
}
},
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier
.fillMaxWidth(),
.padding(horizontal = 20.dp)
.animateContentSize(),
)
if (isClickable && !isExpanded) {
Text(
text = expandText,
fontWeight = FontWeight.Black,
textAlign = TextAlign.End,
style = MaterialTheme.typography.labelLarge,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp),
)
}
}
}
}

@Composable
private fun AvatarWithInterests(
private fun AvatarWithOverlay(
userId: UserId,
nickname: Nickname,
avatarUri: Uri?,
interests: List<Interest>,
isExtendedNetwork: Boolean,
isRequest: Boolean,
modifier: Modifier = Modifier,
) {
Box(modifier = modifier) {
Expand All @@ -278,31 +266,88 @@ private fun AvatarWithInterests(
modifier = Modifier.fillMaxSize(),
)

LazyRow(
Surface(
color = MaterialTheme.colorScheme.surfaceContainerLow,
shape = RoundedCornerShape(
topStart = 20.dp,
topEnd = 20.dp,
),
modifier = Modifier
.align(Alignment.BottomCenter)
.align(Alignment.BottomStart)
.fillMaxWidth(),
) {
itemsIndexed(interests) { i, interest ->
val useDark = isSystemInDarkTheme()
val color = remember(interest.string, useDark) {
Color.pastelFromString(
string = interest.string,
useDark = useDark,
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.height(IntrinsicSize.Min)
.padding(top = 16.dp)
.padding(bottom = 8.dp)
.padding(horizontal = 20.dp),
) {
Text(
text = nickname.string,
style = MaterialTheme.typography.headlineSmall,
fontWeight = FontWeight.SemiBold,
maxLines = 2,
overflow = Ellipsis,
modifier = Modifier.weight(1f, fill = false),
)
if (isRequest) {
SuggestionChip(
onClick = {},
label = {
Text(stringResource(R.string.friend_request))
},
modifier = Modifier
.padding(start = 8.dp)
.height(36.dp),
)
} else if (isExtendedNetwork) {
SuggestionChip(
onClick = {},
label = {
Text(stringResource(R.string.extended_network))
},
modifier = Modifier
.padding(start = 8.dp)
.height(30.dp),
)
}
Spacer(Modifier.width(8.dp))
ElevatedSuggestionChip(
onClick = {},
label = { Text(interest.string) },
colors = SuggestionChipDefaults.suggestionChipColors(
containerColor = color,
labelColor = MaterialTheme.colorScheme.onSurface,
),
}
}
}
}

@Composable
private fun Interests(interests: List<Interest>) {
LazyRow(
modifier = Modifier.fillMaxWidth(),
) {
itemsIndexed(interests) { i, interest ->
val useDark = isSystemInDarkTheme()
val color = remember(interest.string, useDark) {
Color.pastelFromString(
string = interest.string,
useDark = useDark,
)
if (i == interests.lastIndex) {
Spacer(Modifier.width(8.dp))
}
}
if (i == 0) {
Spacer(Modifier.width(20.dp))
} else {
Spacer(Modifier.width(8.dp))
}
SuggestionChip(
onClick = {},
label = { Text(interest.string) },
colors = SuggestionChipDefaults.suggestionChipColors(
containerColor = color,
labelColor = MaterialTheme.colorScheme.onSurface,
),
border = null,
modifier = Modifier.height(28.dp),
)
if (i == interests.lastIndex) {
Spacer(Modifier.width(20.dp))
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/friendly/android/IndicatedCardFeed.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ fun IndicatedCardFeed(
modifier: Modifier = Modifier,
) {
val swipeCardsState = rememberSwipeableCardsState(
visibleCardsInStack = 2,
itemCount = { currentItems.size },
)

Expand All @@ -54,7 +55,7 @@ fun IndicatedCardFeed(
},
modifier = Modifier.fillMaxSize(),
) {
items(currentItems) { item, _, _ ->
items(currentItems) { item, index, offset ->
FeedCard(
entry = item,
like = { entry ->
Expand Down
Loading