mirror of https://github.com/readrops/Readrops.git
Rework TimelineItem UI and add three sizes : compact, regular and large
This commit is contained in:
parent
f57d39ec3b
commit
46800586e1
|
@ -1,42 +1,18 @@
|
||||||
package com.readrops.app.timelime
|
package com.readrops.app.timelime
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.aspectRatio
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.Favorite
|
|
||||||
import androidx.compose.material.icons.outlined.FavoriteBorder
|
|
||||||
import androidx.compose.material.icons.outlined.Share
|
|
||||||
import androidx.compose.material3.Card
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import com.readrops.app.util.DefaultPreview
|
||||||
import androidx.compose.ui.graphics.Color
|
import com.readrops.app.util.theme.ReadropsTheme
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import com.readrops.db.entities.Folder
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import coil.compose.AsyncImage
|
|
||||||
import com.readrops.api.utils.DateUtils
|
|
||||||
import com.readrops.app.R
|
|
||||||
import com.readrops.app.util.components.IconText
|
|
||||||
import com.readrops.app.util.theme.ShortSpacer
|
|
||||||
import com.readrops.app.util.theme.VeryShortSpacer
|
|
||||||
import com.readrops.app.util.theme.spacing
|
|
||||||
import com.readrops.db.pojo.ItemWithFeed
|
import com.readrops.db.pojo.ItemWithFeed
|
||||||
import kotlin.math.roundToInt
|
import org.joda.time.LocalDateTime
|
||||||
|
|
||||||
|
enum class TimelineItemSize {
|
||||||
|
COMPACT,
|
||||||
|
REGULAR,
|
||||||
|
LARGE
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TimelineItem(
|
fun TimelineItem(
|
||||||
|
@ -45,155 +21,94 @@ fun TimelineItem(
|
||||||
onFavorite: () -> Unit,
|
onFavorite: () -> Unit,
|
||||||
onShare: () -> Unit,
|
onShare: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
compactLayout: Boolean = false,
|
size: TimelineItemSize = TimelineItemSize.LARGE,
|
||||||
) {
|
) {
|
||||||
Card(
|
when (size) {
|
||||||
|
TimelineItemSize.COMPACT -> {
|
||||||
|
CompactTimelineItem(
|
||||||
|
itemWithFeed = itemWithFeed,
|
||||||
|
onClick = onClick,
|
||||||
|
onFavorite = onFavorite,
|
||||||
|
onShare = onShare,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.padding(horizontal = MaterialTheme.spacing.shortSpacing)
|
|
||||||
.alpha(if (itemWithFeed.item.isRead) 0.6f else 1f)
|
|
||||||
.clickable { onClick() }
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(
|
|
||||||
start = MaterialTheme.spacing.shortSpacing,
|
|
||||||
end = MaterialTheme.spacing.shortSpacing,
|
|
||||||
top = MaterialTheme.spacing.shortSpacing,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.weight(1f)
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
) {
|
|
||||||
AsyncImage(
|
|
||||||
model = itemWithFeed.feedIconUrl,
|
|
||||||
error = painterResource(id = R.drawable.ic_rss_feed_grey),
|
|
||||||
contentDescription = itemWithFeed.feedName,
|
|
||||||
placeholder = painterResource(R.drawable.ic_rss_feed_grey),
|
|
||||||
modifier = Modifier.size(24.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
VeryShortSpacer()
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = itemWithFeed.feedName,
|
|
||||||
style = MaterialTheme.typography.labelMedium,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
color = if (itemWithFeed.bgColor != 0) Color(itemWithFeed.bgColor) else MaterialTheme.colorScheme.onSurface,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
TimelineItemSize.REGULAR -> {
|
||||||
|
RegularTimelineItem(
|
||||||
Row {
|
itemWithFeed = itemWithFeed,
|
||||||
Surface(
|
onClick = onClick,
|
||||||
color = if (itemWithFeed.bgColor != 0) Color(itemWithFeed.bgColor) else MaterialTheme.colorScheme.primary,
|
onFavorite = onFavorite,
|
||||||
shape = RoundedCornerShape(48.dp)
|
onShare = onShare,
|
||||||
) {
|
modifier = modifier
|
||||||
Text(
|
|
||||||
text = DateUtils.formattedDateByLocal(itemWithFeed.item.pubDate!!),
|
|
||||||
style = MaterialTheme.typography.labelMedium,
|
|
||||||
color = if (itemWithFeed.bgColor != 0) Color.White else MaterialTheme.colorScheme.onPrimary,
|
|
||||||
modifier = Modifier.padding(
|
|
||||||
horizontal = MaterialTheme.spacing.shortSpacing,
|
|
||||||
vertical = MaterialTheme.spacing.veryShortSpacing
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
TimelineItemSize.LARGE -> {
|
||||||
|
LargeTimelineItem(
|
||||||
|
itemWithFeed = itemWithFeed,
|
||||||
|
onClick = onClick,
|
||||||
|
onFavorite = onFavorite,
|
||||||
|
onShare = onShare,
|
||||||
|
modifier = modifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ShortSpacer()
|
private val itemWithFeed = ItemWithFeed(
|
||||||
|
item = com.readrops.db.entities.Item(
|
||||||
Row(
|
title = "This is a not so long item title",
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
pubDate = LocalDateTime.now(),
|
||||||
horizontalArrangement = Arrangement.Start,
|
cleanDescription = """Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||||
modifier = Modifier
|
Donec a tortor neque. Nam ultrices, diam ac congue finibus, tortor sem congue urna,
|
||||||
.fillMaxWidth()
|
at finibus elit libero at mi. Etiam hendrerit sapien eu porta feugiat. Duis porttitor"""
|
||||||
.padding(start = MaterialTheme.spacing.shortSpacing)
|
.replace("\n", "")
|
||||||
) {
|
.trimMargin(),
|
||||||
if (itemWithFeed.folder != null) {
|
imageLink = ""
|
||||||
IconText(
|
),
|
||||||
icon = painterResource(id = R.drawable.ic_folder_grey),
|
feedName = "feed name",
|
||||||
text = itemWithFeed.folder!!.name!!,
|
color = 0,
|
||||||
style = MaterialTheme.typography.labelMedium
|
bgColor = 0,
|
||||||
|
feedId = 0,
|
||||||
|
feedIconUrl = "",
|
||||||
|
websiteUrl = "",
|
||||||
|
folder = Folder(name = "Folder name")
|
||||||
)
|
)
|
||||||
|
|
||||||
Text(
|
@DefaultPreview
|
||||||
text = "·",
|
@Composable
|
||||||
style = MaterialTheme.typography.labelMedium,
|
private fun RegularTimelineItemPreview() {
|
||||||
modifier = Modifier.padding(horizontal = MaterialTheme.spacing.veryShortSpacing)
|
ReadropsTheme {
|
||||||
)
|
RegularTimelineItem(
|
||||||
}
|
itemWithFeed = itemWithFeed,
|
||||||
|
onClick = {},
|
||||||
IconText(
|
onFavorite = {},
|
||||||
icon = painterResource(id = R.drawable.ic_hourglass_empty),
|
onShare = {},
|
||||||
text = if (itemWithFeed.item.readTime < 1) "< 1 min" else "${itemWithFeed.item.readTime.roundToInt()} mins",
|
|
||||||
style = MaterialTheme.typography.labelMedium
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ShortSpacer()
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = itemWithFeed.item.title!!,
|
|
||||||
style = MaterialTheme.typography.titleMedium,
|
|
||||||
maxLines = 2,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
modifier = Modifier.padding(horizontal = MaterialTheme.spacing.shortSpacing)
|
|
||||||
)
|
|
||||||
|
|
||||||
ShortSpacer()
|
|
||||||
|
|
||||||
if (itemWithFeed.item.cleanDescription != null && !compactLayout) {
|
|
||||||
Text(
|
|
||||||
text = itemWithFeed.item.cleanDescription!!,
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
maxLines = 3,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
modifier = Modifier.padding(horizontal = MaterialTheme.spacing.shortSpacing)
|
|
||||||
)
|
|
||||||
|
|
||||||
ShortSpacer()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itemWithFeed.item.hasImage && !compactLayout) {
|
|
||||||
AsyncImage(
|
|
||||||
model = itemWithFeed.item.imageLink,
|
|
||||||
contentDescription = itemWithFeed.item.title!!,
|
|
||||||
contentScale = ContentScale.Crop,
|
|
||||||
modifier = Modifier
|
|
||||||
.aspectRatio(16f / 9f)
|
|
||||||
.fillMaxWidth()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Row(
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(MaterialTheme.spacing.shortSpacing)
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = if (itemWithFeed.item.isStarred) Icons.Filled.Favorite else Icons.Outlined.FavoriteBorder,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.clickable { onFavorite() }
|
|
||||||
)
|
|
||||||
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Outlined.Share,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.clickable { onShare() }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DefaultPreview
|
||||||
|
@Composable
|
||||||
|
private fun CompactTimelineItemPreview() {
|
||||||
|
ReadropsTheme {
|
||||||
|
CompactTimelineItem(
|
||||||
|
itemWithFeed = itemWithFeed,
|
||||||
|
onClick = {},
|
||||||
|
onFavorite = {},
|
||||||
|
onShare = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@DefaultPreview
|
||||||
|
@Composable
|
||||||
|
private fun LargeTimelineItemPreview() {
|
||||||
|
ReadropsTheme {
|
||||||
|
LargeTimelineItem(
|
||||||
|
itemWithFeed = itemWithFeed,
|
||||||
|
onClick = {},
|
||||||
|
onFavorite = {},
|
||||||
|
onShare = {},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,397 @@
|
||||||
|
package com.readrops.app.timelime
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Favorite
|
||||||
|
import androidx.compose.material.icons.outlined.FavoriteBorder
|
||||||
|
import androidx.compose.material.icons.outlined.Share
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
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.ContentScale
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalInspectionMode
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import coil.compose.AsyncImage
|
||||||
|
import coil.request.ImageRequest
|
||||||
|
import com.readrops.api.utils.DateUtils
|
||||||
|
import com.readrops.app.R
|
||||||
|
import com.readrops.app.util.theme.ShortSpacer
|
||||||
|
import com.readrops.app.util.theme.spacing
|
||||||
|
import com.readrops.db.pojo.ItemWithFeed
|
||||||
|
import org.joda.time.LocalDateTime
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RegularTimelineItem(
|
||||||
|
itemWithFeed: ItemWithFeed,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
onFavorite: () -> Unit,
|
||||||
|
onShare: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
TimelineItemContainer(
|
||||||
|
isRead = itemWithFeed.item.isRead,
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(MaterialTheme.spacing.mediumSpacing)
|
||||||
|
) {
|
||||||
|
TimelineItemHeader(
|
||||||
|
feedName = itemWithFeed.feedName,
|
||||||
|
feedIconUrl = itemWithFeed.feedIconUrl,
|
||||||
|
feedColor = itemWithFeed.bgColor,
|
||||||
|
folderName = itemWithFeed.folder?.name,
|
||||||
|
date = itemWithFeed.item.pubDate!!,
|
||||||
|
duration = itemWithFeed.item.readTime,
|
||||||
|
isStarred = itemWithFeed.item.isStarred,
|
||||||
|
onFavorite = onFavorite,
|
||||||
|
onShare = onShare
|
||||||
|
)
|
||||||
|
|
||||||
|
ShortSpacer()
|
||||||
|
|
||||||
|
TimelineItemTitle(title = itemWithFeed.item.title!!)
|
||||||
|
|
||||||
|
ShortSpacer()
|
||||||
|
|
||||||
|
TimelineItemBadge(
|
||||||
|
date = itemWithFeed.item.pubDate!!,
|
||||||
|
duration = itemWithFeed.item.readTime,
|
||||||
|
color = itemWithFeed.bgColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CompactTimelineItem(
|
||||||
|
itemWithFeed: ItemWithFeed,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
onFavorite: () -> Unit,
|
||||||
|
onShare: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.alpha(if (itemWithFeed.item.isRead) 0.6f else 1f)
|
||||||
|
.clickable { onClick() }
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(
|
||||||
|
start = MaterialTheme.spacing.shortSpacing,
|
||||||
|
end = MaterialTheme.spacing.shortSpacing,
|
||||||
|
top = MaterialTheme.spacing.shortSpacing
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
TimelineItemHeader(
|
||||||
|
feedName = itemWithFeed.feedName,
|
||||||
|
feedIconUrl = itemWithFeed.feedIconUrl,
|
||||||
|
feedColor = itemWithFeed.bgColor,
|
||||||
|
folderName = itemWithFeed.folder?.name,
|
||||||
|
onFavorite = onFavorite,
|
||||||
|
onShare = onShare,
|
||||||
|
date = itemWithFeed.item.pubDate!!,
|
||||||
|
duration = itemWithFeed.item.readTime,
|
||||||
|
isStarred = itemWithFeed.item.isStarred,
|
||||||
|
displayActions = false
|
||||||
|
)
|
||||||
|
|
||||||
|
ShortSpacer()
|
||||||
|
|
||||||
|
TimelineItemTitle(title = itemWithFeed.item.title!!)
|
||||||
|
|
||||||
|
ShortSpacer()
|
||||||
|
|
||||||
|
HorizontalDivider(
|
||||||
|
modifier = Modifier.padding(horizontal = MaterialTheme.spacing.shortSpacing)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LargeTimelineItem(
|
||||||
|
itemWithFeed: ItemWithFeed,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
onFavorite: () -> Unit,
|
||||||
|
onShare: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
if (itemWithFeed.item.cleanDescription == null && !itemWithFeed.item.hasImage) {
|
||||||
|
RegularTimelineItem(
|
||||||
|
itemWithFeed = itemWithFeed,
|
||||||
|
onClick = onClick,
|
||||||
|
onFavorite = onFavorite,
|
||||||
|
onShare = onShare
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
TimelineItemContainer(
|
||||||
|
isRead = itemWithFeed.item.isRead,
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(MaterialTheme.spacing.mediumSpacing)
|
||||||
|
) {
|
||||||
|
TimelineItemHeader(
|
||||||
|
feedName = itemWithFeed.feedName,
|
||||||
|
feedIconUrl = itemWithFeed.feedIconUrl,
|
||||||
|
feedColor = itemWithFeed.bgColor,
|
||||||
|
folderName = itemWithFeed.folder?.name,
|
||||||
|
date = itemWithFeed.item.pubDate!!,
|
||||||
|
duration = itemWithFeed.item.readTime,
|
||||||
|
isStarred = itemWithFeed.item.isStarred,
|
||||||
|
onFavorite = onFavorite,
|
||||||
|
onShare = onShare
|
||||||
|
)
|
||||||
|
|
||||||
|
ShortSpacer()
|
||||||
|
|
||||||
|
TimelineItemBadge(
|
||||||
|
date = itemWithFeed.item.pubDate!!,
|
||||||
|
duration = itemWithFeed.item.readTime,
|
||||||
|
color = itemWithFeed.bgColor
|
||||||
|
)
|
||||||
|
|
||||||
|
ShortSpacer()
|
||||||
|
|
||||||
|
TimelineItemTitle(title = itemWithFeed.item.title!!)
|
||||||
|
|
||||||
|
if (itemWithFeed.item.cleanDescription != null) {
|
||||||
|
ShortSpacer()
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = itemWithFeed.item.cleanDescription!!,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
maxLines = 3,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemWithFeed.item.hasImage) {
|
||||||
|
AsyncImage(
|
||||||
|
model = if (!LocalInspectionMode.current) {
|
||||||
|
itemWithFeed.item.imageLink
|
||||||
|
} else {
|
||||||
|
ImageRequest.Builder(LocalContext.current)
|
||||||
|
.data(R.drawable.ic_broken_image)
|
||||||
|
.build()
|
||||||
|
},
|
||||||
|
contentDescription = itemWithFeed.item.title!!,
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
modifier = Modifier
|
||||||
|
.aspectRatio(16f / 9f)
|
||||||
|
.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TimelineItemContainer(
|
||||||
|
isRead: Boolean,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
modifier = modifier
|
||||||
|
.padding(horizontal = MaterialTheme.spacing.shortSpacing)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.alpha(if (isRead) 0.6f else 1f)
|
||||||
|
.clickable { onClick() }
|
||||||
|
) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TimelineItemHeader(
|
||||||
|
feedName: String,
|
||||||
|
feedIconUrl: String?,
|
||||||
|
feedColor: Int,
|
||||||
|
folderName: String?,
|
||||||
|
date: LocalDateTime,
|
||||||
|
duration: Double,
|
||||||
|
isStarred: Boolean,
|
||||||
|
onFavorite: () -> Unit,
|
||||||
|
onShare: () -> Unit,
|
||||||
|
displayActions: Boolean = true
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
) {
|
||||||
|
AsyncImage(
|
||||||
|
model = feedIconUrl,
|
||||||
|
error = painterResource(id = R.drawable.ic_rss_feed_grey),
|
||||||
|
placeholder = painterResource(R.drawable.ic_rss_feed_grey),
|
||||||
|
contentDescription = feedName,
|
||||||
|
modifier = Modifier.size(24.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
ShortSpacer()
|
||||||
|
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = feedName,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
color = if (feedColor != 0) {
|
||||||
|
Color(feedColor)
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.primary
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!folderName.isNullOrEmpty()) {
|
||||||
|
Text(
|
||||||
|
text = folderName,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShortSpacer()
|
||||||
|
|
||||||
|
if (displayActions) {
|
||||||
|
Row {
|
||||||
|
Surface(
|
||||||
|
shape = CircleShape,
|
||||||
|
color = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
onClick = onFavorite
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = if (isStarred) Icons.Filled.Favorite else Icons.Outlined.FavoriteBorder,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShortSpacer()
|
||||||
|
|
||||||
|
Surface(
|
||||||
|
shape = CircleShape,
|
||||||
|
color = MaterialTheme.colorScheme.primaryContainer
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
onClick = onShare
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Share,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.onPrimaryContainer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TimelineItemBadge(
|
||||||
|
date = date,
|
||||||
|
duration = duration,
|
||||||
|
color = feedColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TimelineItemTitle(
|
||||||
|
title: String
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TimelineItemBadge(
|
||||||
|
date: LocalDateTime,
|
||||||
|
duration: Double,
|
||||||
|
color: Int,
|
||||||
|
) {
|
||||||
|
val textColor = if (color != 0) Color.White else MaterialTheme.colorScheme.onPrimary
|
||||||
|
|
||||||
|
|
||||||
|
Surface(
|
||||||
|
color = if (color != 0) Color(color) else MaterialTheme.colorScheme.primary,
|
||||||
|
shape = RoundedCornerShape(48.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.padding(
|
||||||
|
horizontal = MaterialTheme.spacing.shortSpacing,
|
||||||
|
vertical = MaterialTheme.spacing.veryShortSpacing
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = DateUtils.formattedDateByLocal(date),
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
color = textColor
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "·",
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
modifier = Modifier.padding(horizontal = MaterialTheme.spacing.veryShortSpacing),
|
||||||
|
color = textColor
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = if (duration > 1) {
|
||||||
|
stringResource(id = R.string.read_time, duration.roundToInt())
|
||||||
|
} else {
|
||||||
|
stringResource(id = R.string.read_time_lower_than_1)
|
||||||
|
},
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
color = textColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -356,8 +356,9 @@ object TimelineTab : Tab {
|
||||||
onShare = {
|
onShare = {
|
||||||
viewModel.shareItem(itemWithFeed.item, context)
|
viewModel.shareItem(itemWithFeed.item, context)
|
||||||
},
|
},
|
||||||
compactLayout = true
|
size = TimelineItemSize.LARGE
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.readrops.app.util
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
|
||||||
|
@Preview(
|
||||||
|
uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||||
|
showBackground = true
|
||||||
|
)
|
||||||
|
@Preview(
|
||||||
|
uiMode = Configuration.UI_MODE_NIGHT_NO,
|
||||||
|
showBackground = true
|
||||||
|
)
|
||||||
|
annotation class DefaultPreview
|
|
@ -18,7 +18,7 @@
|
||||||
<string name="add_feed_unknownhost_error">Site inconnu</string>
|
<string name="add_feed_unknownhost_error">Site inconnu</string>
|
||||||
<string name="by_author">par %1$s</string>
|
<string name="by_author">par %1$s</string>
|
||||||
<string name="read_time">%1$s min</string>
|
<string name="read_time">%1$s min</string>
|
||||||
<string name="read_time_lower_than_1">Moins d\'une minute</string>
|
<string name="read_time_lower_than_1">< 1 min</string>
|
||||||
<string name="read_time_one_minute">1 min</string>
|
<string name="read_time_one_minute">1 min</string>
|
||||||
<string name="share_article">Partager l\'article</string>
|
<string name="share_article">Partager l\'article</string>
|
||||||
<string name="open_url">Ouvrir le lien</string>
|
<string name="open_url">Ouvrir le lien</string>
|
||||||
|
|
|
@ -19,8 +19,8 @@
|
||||||
<string name="add_feed_connexion_error">Connection error</string>
|
<string name="add_feed_connexion_error">Connection error</string>
|
||||||
<string name="add_feed_unknownhost_error">Unknown host</string>
|
<string name="add_feed_unknownhost_error">Unknown host</string>
|
||||||
<string name="by_author">by %1$s</string>
|
<string name="by_author">by %1$s</string>
|
||||||
<string name="read_time">%1$s mins</string>
|
<string name="read_time">%1$s min</string>
|
||||||
<string name="read_time_lower_than_1">Less than a minute</string>
|
<string name="read_time_lower_than_1"><1 min</string>
|
||||||
<string name="read_time_one_minute">1 min</string>
|
<string name="read_time_one_minute">1 min</string>
|
||||||
<string name="interpoint" translatable="false">·</string>
|
<string name="interpoint" translatable="false">·</string>
|
||||||
<string name="share_article">Share Article</string>
|
<string name="share_article">Share Article</string>
|
||||||
|
|
Loading…
Reference in New Issue