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
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,7 @@ data class Article(
val wordCount: Int = item?.wordCount ?: 0
val wordCountFull: Int = item?.wordCountFull ?: 0
val image: ThumbnailImage? = item?.thumbnailImage
val isUnread: Boolean = item?.readTime == null
}

enum class TextToDisplay {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ import java.time.ZoneId
import java.time.ZonedDateTime
import java.util.Date
import java.util.TimeZone
import kotlin.time.Duration.Companion.milliseconds

@Composable
fun ArticleScreen(
Expand All @@ -117,11 +118,10 @@ fun ArticleScreen(
val isAnimatedPaging by mavm.isAnimatedPaging.collectAsStateWithLifecycle()

val articleScrollState = rememberScrollState(initial = viewModel.scrollPosition)
val coroutineScope = rememberCoroutineScope()

LaunchedEffect(articleScrollState) {
snapshotFlow { articleScrollState.value }
.debounce(500)
.debounce(500.milliseconds)
.collect { viewModel.saveScrollPosition(it) }
}

Expand Down Expand Up @@ -519,6 +519,8 @@ fun ArticleScreen(
onFeedTitleClick = onFeedTitleClick,
onOpenAudioPlayer = onOpenAudioPlayer,
onOpenInCustomTab = onOpenInCustomTab,
onBookmark = onToggleBookmark,
onMarkAsUnread = onMarkAsUnread,
modifier =
Modifier
.focusGroup()
Expand Down Expand Up @@ -644,6 +646,8 @@ fun ArticleContent(
onFeedTitleClick: () -> Unit,
onOpenAudioPlayer: (url: String) -> Unit,
onOpenInCustomTab: () -> Unit,
onBookmark: () -> Unit,
onMarkAsUnread: () -> Unit,
articleScrollState: ScrollState,
modifier: Modifier = Modifier,
) {
Expand Down Expand Up @@ -673,6 +677,10 @@ fun ArticleContent(
enclosure = viewState.enclosure,
articleTitle = viewState.articleTitle,
feedTitle = viewState.feedDisplayTitle,
saved = viewState.isBookmarked,
onSaveClick = onBookmark,
isUnread = viewState.isUnread,
onMarkAsUnread = onMarkAsUnread,
authorDate =
when {
viewState.author == null && viewState.pubDate != null ->
Expand All @@ -684,7 +692,7 @@ fun ArticleContent(
viewState.author != null && viewState.pubDate != null ->
stringResource(
R.string.by_author_on_date,
// Must wrap author in unicode marks to ensure it formats
// Must wrap author in Unicode marks to ensure it formats
// correctly in RTL
context.unicodeWrap(viewState.author ?: ""),
formatArticleDate(context, viewState.pubDate),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ class ArticleViewModel(
showToolbarMenu = toolbarVisible,
feedDisplayTitle = article?.feedDisplayTitle ?: "",
isBookmarked = article?.bookmarked == true,
isUnread = article?.isUnread == true,
wordCount =
if (isFullText) {
article?.wordCountFull ?: 0
Expand Down Expand Up @@ -923,6 +924,7 @@ private data class ArticleState(
override val showToolbarMenu: Boolean = false,
override val feedDisplayTitle: String = "",
override val isBookmarked: Boolean = false,
override val isUnread: Boolean = true,
override val keyHolder: ArticleItemKeyHolder = RotatingArticleItemKeyHolder,
override val wordCount: Int = 0,
override val image: ThumbnailImage? = null,
Expand Down Expand Up @@ -959,6 +961,7 @@ interface ArticleScreenViewState {
val showToolbarMenu: Boolean
val feedDisplayTitle: String
val isBookmarked: Boolean
val isUnread: Boolean
val keyHolder: ArticleItemKeyHolder
val wordCount: Int
val image: ThumbnailImage?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package com.nononsenseapps.feeder.ui.compose.feedarticle

import android.util.Log
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.clickable
Expand All @@ -16,19 +21,26 @@ import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
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.rememberScrollState
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ContentAlpha
import androidx.compose.material.LocalContentAlpha
import androidx.compose.material.Surface
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.BookmarkAdded
import androidx.compose.material.icons.outlined.BookmarkBorder
import androidx.compose.material.icons.outlined.ErrorOutline
import androidx.compose.material.icons.outlined.Terrain
import androidx.compose.material.icons.outlined.Timelapse
import androidx.compose.material.icons.outlined.VisibilityOff
import androidx.compose.material3.FilterChip
import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SuggestionChip
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
Expand Down Expand Up @@ -73,16 +85,8 @@ import com.nononsenseapps.feeder.ui.compose.utils.ScreenType
import com.nononsenseapps.feeder.ui.compose.utils.focusableInNonTouchMode
import my.nanihadesuka.compose.ColumnScrollbar
import my.nanihadesuka.compose.ScrollbarSettings
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import java.util.Locale
import kotlin.math.roundToInt

val dateTimeFormat: DateTimeFormatter =
DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.SHORT)
.withLocale(Locale.getDefault())

@Composable
fun ReaderView(
screenType: ScreenType,
Expand All @@ -92,6 +96,10 @@ fun ReaderView(
enclosure: Enclosure,
articleTitle: String,
feedTitle: String,
saved: Boolean,
onSaveClick: () -> Unit,
isUnread: Boolean,
onMarkAsUnread: () -> Unit,
authorDate: String?,
image: ThumbnailImage?,
showHeaderImage: Boolean,
Expand Down Expand Up @@ -210,6 +218,66 @@ fun ReaderView(
}
}
}
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.width(dimens.maxReaderWidth),
) {
FilterChip(
selected = saved,
leadingIcon =
{
// Filter chip only animates between null and non-null values by itself so animate manually
AnimatedContent(
targetState = saved,
transitionSpec = { fadeIn() togetherWith fadeOut() },
) { target ->
Icon(
imageVector =
if (target) {
Icons.Outlined.BookmarkAdded
} else {
Icons.Outlined.BookmarkBorder
},
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize),
)
}
},
onClick = onSaveClick,
label = {
AnimatedContent(
targetState = saved,
) { target ->
Text(
if (target) {
stringResource(R.string.saved_adjective)
} else {
stringResource(R.string.save_article)
},
)
}
},
)
AnimatedVisibility(
visible = !isUnread,
exit = fadeOut(),
) {
SuggestionChip(
icon =
{
Icon(
imageVector = Icons.Outlined.VisibilityOff,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize),
)
},
onClick = onMarkAsUnread,
label = {
Text(stringResource(R.string.mark_as_unread))
},
)
}
}
if (readTimeSecs > 0) {
ProvideScaledText(style = MaterialTheme.typography.titleMedium) {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Expand Down Expand Up @@ -383,6 +451,10 @@ private fun ReaderPreview() {
enclosure = Enclosure(),
articleTitle = "Article title on top",
feedTitle = "Feed Title is here",
saved = true,
onSaveClick = {},
isUnread = false,
onMarkAsUnread = {},
authorDate = "2018-01-02",
image = MediaImage("https://cowboyprogrammer.org/images/2017/10/gimp_image_mode_index.png"),
showHeaderImage = true,
Expand Down
Loading