|
|
|
|
@ -14,6 +14,8 @@
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
@file:OptIn(ExperimentalResourceApi::class)
|
|
|
|
|
|
|
|
|
|
package com.google.samples.apps.nowinandroid.core.ui
|
|
|
|
|
|
|
|
|
|
import androidx.compose.foundation.Canvas
|
|
|
|
|
@ -48,17 +50,15 @@ import androidx.compose.ui.Modifier
|
|
|
|
|
import androidx.compose.ui.graphics.Color
|
|
|
|
|
import androidx.compose.ui.layout.ContentScale
|
|
|
|
|
import androidx.compose.ui.platform.LocalInspectionMode
|
|
|
|
|
import androidx.compose.ui.res.painterResource
|
|
|
|
|
import androidx.compose.ui.res.stringResource
|
|
|
|
|
import androidx.compose.ui.semantics.contentDescription
|
|
|
|
|
import androidx.compose.ui.semantics.onClick
|
|
|
|
|
import androidx.compose.ui.semantics.semantics
|
|
|
|
|
import androidx.compose.ui.tooling.preview.Preview
|
|
|
|
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
|
|
|
|
import androidx.compose.ui.text.intl.Locale
|
|
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
|
|
import coil.compose.AsyncImagePainter
|
|
|
|
|
import coil.compose.rememberAsyncImagePainter
|
|
|
|
|
import com.google.samples.apps.nowinandroid.core.designsystem.R.drawable
|
|
|
|
|
import coil3.ImageLoader
|
|
|
|
|
import coil3.PlatformContext
|
|
|
|
|
import coil3.compose.AsyncImagePainter
|
|
|
|
|
import coil3.compose.rememberAsyncImagePainter
|
|
|
|
|
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaIconToggleButton
|
|
|
|
|
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTopicTag
|
|
|
|
|
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
|
|
|
|
|
@ -67,16 +67,22 @@ import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic
|
|
|
|
|
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
|
|
|
|
|
import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource
|
|
|
|
|
import kotlinx.datetime.Instant
|
|
|
|
|
import kotlinx.datetime.toJavaInstant
|
|
|
|
|
import kotlinx.datetime.toJavaZoneId
|
|
|
|
|
import java.time.format.DateTimeFormatter
|
|
|
|
|
import java.time.format.FormatStyle
|
|
|
|
|
import java.util.Locale
|
|
|
|
|
import nowinandroid.core.ui.generated.resources.Res
|
|
|
|
|
import nowinandroid.core.ui.generated.resources.core_ui_bookmark
|
|
|
|
|
import nowinandroid.core.ui.generated.resources.core_ui_card_meta_data_text
|
|
|
|
|
import nowinandroid.core.ui.generated.resources.core_ui_card_tap_action
|
|
|
|
|
import nowinandroid.core.ui.generated.resources.core_ui_topic_chip_content_description_when_followed
|
|
|
|
|
import nowinandroid.core.ui.generated.resources.core_ui_topic_chip_content_description_when_not_followed
|
|
|
|
|
import nowinandroid.core.ui.generated.resources.core_ui_unbookmark
|
|
|
|
|
import nowinandroid.core.ui.generated.resources.core_ui_unread_resource_dot_content_description
|
|
|
|
|
import org.jetbrains.compose.resources.ExperimentalResourceApi
|
|
|
|
|
import org.jetbrains.compose.resources.stringResource
|
|
|
|
|
import org.jetbrains.compose.ui.tooling.preview.Preview
|
|
|
|
|
import org.jetbrains.compose.ui.tooling.preview.PreviewParameter
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [NewsResource] card used on the following screens: For You, Saved
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
|
fun NewsResourceCardExpanded(
|
|
|
|
|
userNewsResource: UserNewsResource,
|
|
|
|
|
@ -87,7 +93,7 @@ fun NewsResourceCardExpanded(
|
|
|
|
|
onTopicClick: (String) -> Unit,
|
|
|
|
|
modifier: Modifier = Modifier,
|
|
|
|
|
) {
|
|
|
|
|
val clickActionLabel = stringResource(R.string.core_ui_card_tap_action)
|
|
|
|
|
val clickActionLabel = stringResource(Res.string.core_ui_card_tap_action)
|
|
|
|
|
Card(
|
|
|
|
|
onClick = onClick,
|
|
|
|
|
shape = RoundedCornerShape(16.dp),
|
|
|
|
|
@ -147,12 +153,13 @@ fun NewsResourceHeaderImage(
|
|
|
|
|
) {
|
|
|
|
|
var isLoading by remember { mutableStateOf(true) }
|
|
|
|
|
var isError by remember { mutableStateOf(false) }
|
|
|
|
|
val imageLoader = rememberAsyncImagePainter(
|
|
|
|
|
val imagePainter = rememberAsyncImagePainter(
|
|
|
|
|
model = headerImageUrl,
|
|
|
|
|
onState = { state ->
|
|
|
|
|
isLoading = state is AsyncImagePainter.State.Loading
|
|
|
|
|
isError = state is AsyncImagePainter.State.Error
|
|
|
|
|
},
|
|
|
|
|
imageLoader = imageLoader,
|
|
|
|
|
)
|
|
|
|
|
val isLocalInspection = LocalInspectionMode.current
|
|
|
|
|
Box(
|
|
|
|
|
@ -176,11 +183,12 @@ fun NewsResourceHeaderImage(
|
|
|
|
|
.fillMaxWidth()
|
|
|
|
|
.height(180.dp),
|
|
|
|
|
contentScale = ContentScale.Crop,
|
|
|
|
|
painter = if (isError.not() && !isLocalInspection) {
|
|
|
|
|
imageLoader
|
|
|
|
|
} else {
|
|
|
|
|
painterResource(drawable.core_designsystem_ic_placeholder_default)
|
|
|
|
|
},
|
|
|
|
|
painter =
|
|
|
|
|
// if (isError.not() && !isLocalInspection) {
|
|
|
|
|
imagePainter,
|
|
|
|
|
// } else {
|
|
|
|
|
// painterResource(drawable.core_designsystem_ic_placeholder_default)
|
|
|
|
|
// },
|
|
|
|
|
// TODO b/226661685: Investigate using alt text of image to populate content description
|
|
|
|
|
// decorative image,
|
|
|
|
|
contentDescription = null,
|
|
|
|
|
@ -209,13 +217,13 @@ fun BookmarkButton(
|
|
|
|
|
icon = {
|
|
|
|
|
Icon(
|
|
|
|
|
imageVector = NiaIcons.BookmarkBorder,
|
|
|
|
|
contentDescription = stringResource(R.string.core_ui_bookmark),
|
|
|
|
|
contentDescription = stringResource(Res.string.core_ui_bookmark),
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
checkedIcon = {
|
|
|
|
|
Icon(
|
|
|
|
|
imageVector = NiaIcons.Bookmark,
|
|
|
|
|
contentDescription = stringResource(R.string.core_ui_unbookmark),
|
|
|
|
|
contentDescription = stringResource(Res.string.core_ui_unbookmark),
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
@ -226,7 +234,7 @@ fun NotificationDot(
|
|
|
|
|
color: Color,
|
|
|
|
|
modifier: Modifier = Modifier,
|
|
|
|
|
) {
|
|
|
|
|
val description = stringResource(R.string.core_ui_unread_resource_dot_content_description)
|
|
|
|
|
val description = stringResource(Res.string.core_ui_unread_resource_dot_content_description)
|
|
|
|
|
Canvas(
|
|
|
|
|
modifier = modifier
|
|
|
|
|
.semantics { contentDescription = description },
|
|
|
|
|
@ -240,11 +248,7 @@ fun NotificationDot(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
|
fun dateFormatted(publishDate: Instant): String = DateTimeFormatter
|
|
|
|
|
.ofLocalizedDate(FormatStyle.MEDIUM)
|
|
|
|
|
.withLocale(Locale.getDefault())
|
|
|
|
|
.withZone(LocalTimeZone.current.toJavaZoneId())
|
|
|
|
|
.format(publishDate.toJavaInstant())
|
|
|
|
|
fun dateFormatted(publishDate: Instant): String = publishDate.toString()
|
|
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
|
fun NewsResourceMetaData(
|
|
|
|
|
@ -254,7 +258,7 @@ fun NewsResourceMetaData(
|
|
|
|
|
val formattedDate = dateFormatted(publishDate)
|
|
|
|
|
Text(
|
|
|
|
|
if (resourceType.isNotBlank()) {
|
|
|
|
|
stringResource(R.string.core_ui_card_meta_data_text, formattedDate, resourceType)
|
|
|
|
|
stringResource(Res.string.core_ui_card_meta_data_text, formattedDate, resourceType)
|
|
|
|
|
} else {
|
|
|
|
|
formattedDate
|
|
|
|
|
},
|
|
|
|
|
@ -287,17 +291,17 @@ fun NewsResourceTopics(
|
|
|
|
|
text = {
|
|
|
|
|
val contentDescription = if (followableTopic.isFollowed) {
|
|
|
|
|
stringResource(
|
|
|
|
|
R.string.core_ui_topic_chip_content_description_when_followed,
|
|
|
|
|
Res.string.core_ui_topic_chip_content_description_when_followed,
|
|
|
|
|
followableTopic.topic.name,
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
stringResource(
|
|
|
|
|
R.string.core_ui_topic_chip_content_description_when_not_followed,
|
|
|
|
|
Res.string.core_ui_topic_chip_content_description_when_not_followed,
|
|
|
|
|
followableTopic.topic.name,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
Text(
|
|
|
|
|
text = followableTopic.topic.name.uppercase(Locale.getDefault()),
|
|
|
|
|
text = followableTopic.topic.name.uppercase(),
|
|
|
|
|
modifier = Modifier.semantics {
|
|
|
|
|
this.contentDescription = contentDescription
|
|
|
|
|
},
|
|
|
|
|
@ -308,7 +312,8 @@ fun NewsResourceTopics(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Preview("Bookmark Button")
|
|
|
|
|
//@Preview("Bookmark Button")
|
|
|
|
|
@Preview
|
|
|
|
|
@Composable
|
|
|
|
|
private fun BookmarkButtonPreview() {
|
|
|
|
|
NiaTheme {
|
|
|
|
|
@ -318,7 +323,8 @@ private fun BookmarkButtonPreview() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Preview("Bookmark Button Bookmarked")
|
|
|
|
|
//@Preview("Bookmark Button Bookmarked")
|
|
|
|
|
@Preview
|
|
|
|
|
@Composable
|
|
|
|
|
private fun BookmarkButtonBookmarkedPreview() {
|
|
|
|
|
NiaTheme {
|
|
|
|
|
@ -328,7 +334,8 @@ private fun BookmarkButtonBookmarkedPreview() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Preview("NewsResourceCardExpanded")
|
|
|
|
|
@Preview
|
|
|
|
|
//@Preview("NewsResourceCardExpanded")
|
|
|
|
|
@Composable
|
|
|
|
|
private fun ExpandedNewsResourcePreview(
|
|
|
|
|
@PreviewParameter(UserNewsResourcePreviewParameterProvider::class)
|