Skip to content

Commit

Permalink
Add icon button to toggle article star
Browse files Browse the repository at this point in the history
  • Loading branch information
jocmp committed Jan 3, 2024
1 parent 609479d commit 027b2f2
Show file tree
Hide file tree
Showing 13 changed files with 159 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class AccountViewModel(

fun selectArticle(articleID: String) {
account.markRead(articleID)
articleState.value = account.findArticle(articleID.toLong())
articleState.value = account.findArticle(articleID = articleID)
}

fun toggleArticleRead() {
Expand All @@ -104,6 +104,18 @@ class AccountViewModel(
}
}

fun toggleArticleStar() {
articleState.value?.let { article ->
if (article.starred) {
account.removeStar(article.id)
} else {
account.addStar(article.id)
}

articleState.value = article.copy(starred = !article.starred)
}
}

fun clearArticle() {
articleState.value = null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ fun ArticleFilterNavigationBar(

NavigationBar {
NavigationBarItem(
icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
icon = {
Icon(
painterResource(R.drawable.icon_star_filled),
contentDescription = null
)
},
label = { Text(stringResource(id = R.string.article_filters_starred)) },
selected = selected === ArticleStatus.STARRED,
onClick = { checkedSelect(ArticleStatus.STARRED) },
Expand All @@ -35,7 +40,7 @@ fun ArticleFilterNavigationBar(
NavigationBarItem(
icon = {
Icon(
painter = painterResource(R.drawable.unread),
painterResource(R.drawable.icon_circle_filled),
contentDescription = null
)
},
Expand All @@ -47,7 +52,7 @@ fun ArticleFilterNavigationBar(
NavigationBarItem(
icon = {
Icon(
painter = painterResource(R.drawable.notes),
painter = painterResource(R.drawable.icon_notes),
contentDescription = null
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ fun ArticleScreen(
ArticleView(
article = viewModel.article,
onToggleRead = viewModel::toggleArticleRead,
onToggleStar = viewModel::toggleArticleStar,
onBackPressed = {
viewModel.clearArticle()
setDestination(ListDetailPaneScaffoldRole.List)
Expand Down
34 changes: 26 additions & 8 deletions app/src/main/java/com/jocmp/basilreader/ui/articles/ArticleView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@ package com.jocmp.basilreader.ui.articles

import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.material.icons.filled.Phone
import androidx.compose.material.icons.filled.Star
import androidx.compose.material.icons.outlined.CheckCircle
import androidx.compose.material.icons.outlined.Phone
import androidx.compose.material.icons.outlined.Star
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import com.jocmp.basil.Article
import com.jocmp.basilreader.R
import com.jocmp.basilreader.ui.components.EmptyView
import com.jocmp.basilreader.ui.components.WebView
import com.jocmp.basilreader.ui.components.rememberWebViewStateWithHTMLData
Expand All @@ -23,11 +27,13 @@ fun ArticleView(
article: Article?,
onBackPressed: () -> Unit,
onToggleRead: () -> Unit,
onToggleStar: () -> Unit
) {
if (article != null) {
ArticleLoadedView(
article = article,
onToggleRead = onToggleRead
onToggleRead = onToggleRead,
onToggleStar = onToggleStar
)
} else {
EmptyView()
Expand All @@ -42,19 +48,31 @@ fun ArticleView(
fun ArticleLoadedView(
article: Article,
onToggleRead: () -> Unit,
onToggleStar: () -> Unit
) {
val state = rememberWebViewStateWithHTMLData(article.contentHTML)

val image = if (article.read) {
Icons.Outlined.CheckCircle
val readIcon = if (article.read) {
R.drawable.icon_circle_outline
} else {
Icons.Filled.CheckCircle
R.drawable.icon_circle_filled
}

val starIcon = if (article.starred) {
R.drawable.icon_star_filled
} else {
R.drawable.icon_star_outline
}

Scaffold(
topBar = {
Button(onClick = { onToggleRead() }) {
Icon(imageVector = image, contentDescription = null)
Row {
IconButton(onClick = { onToggleRead() }) {
Icon(painterResource(id = readIcon), contentDescription = null)
}
IconButton(onClick = { onToggleStar() }) {
Icon(painterResource(id = starIcon), contentDescription = null)
}
}
}
) { innerPadding ->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:width="20dp"
android:height="20dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/icon_circle_outline.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FF000000"
android:pathData="M480,880q-83,0 -156,-31.5T197,763q-54,-54 -85.5,-127T80,480q0,-83 31.5,-156T197,197q54,-54 127,-85.5T480,80q83,0 156,31.5T763,197q54,54 85.5,127T880,480q0,83 -31.5,156T763,763q-54,54 -127,85.5T480,880ZM480,800q134,0 227,-93t93,-227q0,-134 -93,-227t-227,-93q-134,0 -227,93t-93,227q0,134 93,227t227,93ZM480,480Z"/>
</vector>
File renamed without changes.
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/icon_star_filled.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FF000000"
android:pathData="m233,880 l65,-281L80,410l288,-25 112,-265 112,265 288,25 -218,189 65,281 -247,-149L233,880Z"/>
</vector>
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/icon_star_outline.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FF000000"
android:pathData="m354,713 l126,-76 126,77 -33,-144 111,-96 -146,-13 -58,-136 -58,135 -146,13 111,97 -33,143ZM233,880l65,-281L80,410l288,-25 112,-265 112,265 288,25 -218,189 65,281 -247,-149L233,880ZM480,530Z"/>
</vector>
17 changes: 10 additions & 7 deletions basil/src/main/java/com/jocmp/basil/Account.kt
Original file line number Diff line number Diff line change
Expand Up @@ -142,21 +142,24 @@ data class Account(
return folders.find { it.title == title }
}

fun findArticle(articleID: Long?): Article? {
articleID ?: return null
fun findArticle(articleID: String): Article? {
return articles.fetch(articleID = articleID)
}

fun addStar(articleID: String) {
articles.addStar(articleID = articleID)
}

return database.articlesQueries.findBy(
articleID = articleID,
mapper = ::articleMapper
).executeAsOneOrNull()
fun removeStar(articleID: String) {
articles.removeStar(articleID = articleID)
}

fun markRead(articleID: String) {
articles.markRead(articleID)
}

fun markUnread(articleID: String) {
articles.markUnread(articleID)
articles.markUnread(articleID = articleID)
}

private fun updateArticles(feed: Feed, items: List<ParsedItem>) {
Expand Down
25 changes: 25 additions & 0 deletions basil/src/main/java/com/jocmp/basil/persistence/ArticleRecords.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ class ArticleRecords internal constructor(
val byStatus = ByStatus(database)
val byFeed = ByFeed(database)

fun fetch(articleID: String): Article? {
val id = articleID.toLongOrNull()

id ?: return null

return database.articlesQueries.findBy(
articleID = id,
mapper = ::articleMapper
).executeAsOneOrNull()
}

fun markRead(articleID: String, lastReadAt: ZonedDateTime = ZonedDateTime.now()) {
database.articlesQueries.markRead(
articleID = articleID.toLong(),
Expand All @@ -28,6 +39,20 @@ class ArticleRecords internal constructor(
)
}

fun addStar(articleID: String) {
database.articlesQueries.markStarred(
articleID = articleID.toLong(),
starred = true
)
}

fun removeStar(articleID: String) {
database.articlesQueries.markStarred(
articleID = articleID.toLong(),
starred = false
)
}

class ByFeed(private val database: Database) {
fun all(
feedIDs: List<Long>,
Expand Down
10 changes: 10 additions & 0 deletions basil/src/main/sqldelight/com/jocmp/basil/db/articles.sq
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,13 @@ WHERE EXISTS (
AND articles.external_id = article_statuses.external_id AND article_statuses.feed_id = articles.feed_id
LIMIT 1
);

markStarred:
UPDATE article_statuses SET starred = :starred
WHERE EXISTS (
SELECT id
FROM articles
WHERE articles.id = :articleID
AND articles.external_id = article_statuses.external_id AND article_statuses.feed_id = articles.feed_id
LIMIT 1
);
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import com.jocmp.basil.fixtures.ArticleFixture
import com.jocmp.basil.repeated
import org.junit.Before
import org.junit.Test
import kotlin.math.exp
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class ArticleRecordsTest {
Expand Down Expand Up @@ -75,4 +75,40 @@ class ArticleRecordsTest {
assertEquals(expected = 2, actual = count)
assertEquals(actual = actual, expected = expected)
}

@Test
fun markUnread() {
val article = articleFixture.create()
val articleRecords = ArticleRecords(database)

articleRecords.markUnread(articleID = article.id)

val reloaded = articleRecords.fetch(articleID = article.id)!!

assertFalse(reloaded.read)
}

@Test
fun addStar() {
val article = articleFixture.create()
val articleRecords = ArticleRecords(database)

articleRecords.addStar(articleID = article.id)

val reloaded = articleRecords.fetch(articleID = article.id)!!

assertTrue(reloaded.starred)
}

@Test
fun removeStar() {
val article = articleFixture.create()
val articleRecords = ArticleRecords(database)

articleRecords.removeStar(articleID = article.id)

val reloaded = articleRecords.fetch(articleID = article.id)!!

assertFalse(reloaded.starred)
}
}

0 comments on commit 027b2f2

Please sign in to comment.