Fixed article loading and LazyColumn problems
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: Balazs Toldi <balazs@toldi.eu>
This commit is contained in:
parent
a4b3e7e80a
commit
103a253ac0
6 changed files with 135 additions and 56 deletions
|
@ -64,7 +64,8 @@ class MainActivity : ComponentActivity() {
|
|||
|
||||
val scope = rememberCoroutineScope()
|
||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
val articleList = viewModel.articles.observeAsState(emptyList())
|
||||
val articleList = viewModel.articles
|
||||
val feedList = viewModel.feeds.observeAsState()
|
||||
Scaffold(
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(
|
||||
|
@ -96,12 +97,17 @@ class MainActivity : ComponentActivity() {
|
|||
.fillMaxWidth()
|
||||
|
||||
) {
|
||||
items(articleList.value.size) { index ->
|
||||
val articleAndFeed = articleList.value[index]
|
||||
articleAndFeed.articles.sortedByDescending { it.pubDate }
|
||||
.forEach {
|
||||
showArticle(articleAndFeed.feed, article = it)
|
||||
}
|
||||
items(
|
||||
count = articleList.value.size,
|
||||
key = { index ->
|
||||
articleList.value[index].articleId as Long
|
||||
}
|
||||
) { index ->
|
||||
Log.e(null, articleList.value.size.toString())
|
||||
val article = articleList.value[index]
|
||||
val feed =
|
||||
feedList.value?.first() { it.feedId == article.feedId }
|
||||
feed?.let { it1 -> showArticle(it1, article = article) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +152,7 @@ class MainActivity : ComponentActivity() {
|
|||
) {
|
||||
Text(
|
||||
|
||||
text = if (selectedIndex == 0) "None" else viewModel.feedsState.value!!.filterIsInstance<FeedGroup>()[selectedIndex - 1].name.toString(),
|
||||
text = if (selectedIndex == 0) "None" else viewModel.feeds.value!!.filterIsInstance<FeedGroup>()[selectedIndex - 1].name.toString(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = { expanded = true })
|
||||
|
@ -163,13 +169,13 @@ class MainActivity : ComponentActivity() {
|
|||
Text(text = "None")
|
||||
}
|
||||
|
||||
viewModel.feedsState.value!!.filterIsInstance<FeedGroup>()
|
||||
viewModel.feeds.value!!.filterIsInstance<FeedGroup>()
|
||||
.forEachIndexed { index, s ->
|
||||
DropdownMenuItem(onClick = {
|
||||
selectedIndex = index + 1
|
||||
expanded = false
|
||||
}) {
|
||||
viewModel.feedsState.value!![index].name?.let {
|
||||
viewModel.feeds.value!![index].name?.let {
|
||||
Text(text = it)
|
||||
}
|
||||
}
|
||||
|
@ -197,7 +203,7 @@ class MainActivity : ComponentActivity() {
|
|||
viewModel.addFeed(feed)
|
||||
else {
|
||||
var counter = 0
|
||||
viewModel.feedsState.value!!.forEachIndexed { index, feedGroup ->
|
||||
viewModel.feeds.value!!.forEachIndexed { index, feedGroup ->
|
||||
if (feedGroup is FeedGroup) {
|
||||
counter++
|
||||
if (counter == selectedIndex) {
|
||||
|
@ -262,17 +268,8 @@ class MainActivity : ComponentActivity() {
|
|||
Modifier
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onTap = {
|
||||
WebView.webURL = article.url.toString()
|
||||
WebView.barTitle =
|
||||
feed.name ?: ""
|
||||
startActivity(
|
||||
Intent(
|
||||
baseContext,
|
||||
WebView::class.java
|
||||
)
|
||||
)
|
||||
},
|
||||
|
||||
onTap = { openArticle(article.url.toString(), feed.name.toString()) },
|
||||
onLongPress = {
|
||||
val share =
|
||||
Intent.createChooser(Intent().apply {
|
||||
|
@ -397,10 +394,7 @@ class MainActivity : ComponentActivity() {
|
|||
.fillMaxWidth()
|
||||
.padding(vertical = 5.dp, horizontal = 4.dp)
|
||||
.clickable {
|
||||
/*feeds.limit = if (feeds.limit == feed) {
|
||||
null
|
||||
} else feed*/
|
||||
viewModel.changeFeed()
|
||||
viewModel.setLimit(feed)
|
||||
},
|
||||
elevation = 10.dp,
|
||||
backgroundColor = MaterialTheme.colors.surface,
|
||||
|
@ -470,7 +464,8 @@ class MainActivity : ComponentActivity() {
|
|||
drawerState: DrawerState,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val feedList = viewModel.feedsState?.value ?: emptyList()
|
||||
val feedLiveData = viewModel.feeds.observeAsState(emptyList())
|
||||
val feedList = feedLiveData.value
|
||||
ModalDrawer(drawerState = drawerState, drawerContent = {
|
||||
LazyColumn(modifier = Modifier.fillMaxHeight()) {
|
||||
item {
|
||||
|
@ -479,8 +474,7 @@ class MainActivity : ComponentActivity() {
|
|||
.fillMaxWidth()
|
||||
.padding(vertical = 5.dp, horizontal = 4.dp)
|
||||
.clickable {
|
||||
//feeds.limit = null
|
||||
viewModel.changeFeed()
|
||||
viewModel.setLimit(null)
|
||||
},
|
||||
elevation = 10.dp,
|
||||
backgroundColor = MaterialTheme.colors.surface,
|
||||
|
@ -534,6 +528,16 @@ class MainActivity : ComponentActivity() {
|
|||
}
|
||||
}
|
||||
}*/
|
||||
fun openArticle(articleURL: String, feedName: String) {
|
||||
WebView.webURL = articleURL
|
||||
WebView.barTitle = feedName
|
||||
startActivity(
|
||||
Intent(
|
||||
baseContext,
|
||||
WebView::class.java
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package eu.toldi.balazs.anotherfeedreader.entities
|
||||
|
||||
import android.util.Log
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Ignore
|
||||
|
@ -10,8 +11,10 @@ import org.jsoup.Jsoup
|
|||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.Node
|
||||
import org.w3c.dom.NodeList
|
||||
import java.time.LocalDateTime
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.ZoneOffset
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
@Entity(tableName = "article")
|
||||
|
@ -52,12 +55,17 @@ data class Article(
|
|||
val feed: Feed?
|
||||
) {
|
||||
|
||||
var pubDate: ZonedDateTime?
|
||||
get() = pubDate_stamp?.let {
|
||||
ZonedDateTime.ofInstant(
|
||||
Instant.ofEpochSecond(pubDate_stamp!!),
|
||||
ZoneId.systemDefault()
|
||||
)
|
||||
}
|
||||
set(value) {
|
||||
pubDate_stamp = value?.toLocalDateTime()?.toEpochSecond(ZoneOffset.UTC)
|
||||
}
|
||||
|
||||
@Ignore
|
||||
val pubDate : LocalDateTime?
|
||||
init {
|
||||
pubDate = pubDate_stamp?.let { LocalDateTime.ofEpochSecond(it,0, ZoneOffset.UTC) }
|
||||
}
|
||||
override fun toString(): String {
|
||||
return "Article{" +
|
||||
"title='" + title + '\'' +
|
||||
|
@ -68,12 +76,21 @@ data class Article(
|
|||
", pubDate=" + pubDate +
|
||||
'}'
|
||||
}
|
||||
constructor() : this(null,"","","","","",null,null){
|
||||
|
||||
constructor() : this(null, "", "", "", "", "", null, null) {
|
||||
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return if (other is Article) {
|
||||
return title == other.title && url == other.url
|
||||
} else return false
|
||||
}
|
||||
|
||||
|
||||
@ColumnInfo(name = "containingFeedId")
|
||||
var feedId: Long = 0
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Cikk beolvasása egy megadott Node objektumból. Egy hírcsatorna beolvasásakor könnyedén alkalmazható egy "item" Node-ra
|
||||
|
@ -86,7 +103,7 @@ data class Article(
|
|||
var description: String? = null
|
||||
var imageURL: String? = null
|
||||
var author: String? = null
|
||||
var pubDate: LocalDateTime? = null
|
||||
var pubDate: ZonedDateTime? = null
|
||||
for (j in 0 until properties.getLength()) {
|
||||
val node: Node = properties.item(j)
|
||||
val tagName: String = node.getNodeName()
|
||||
|
@ -94,7 +111,7 @@ data class Article(
|
|||
when (tagName) {
|
||||
"title" -> title = node.getTextContent()
|
||||
"author", "dc:creator" -> author = node.getTextContent()
|
||||
"pubDate" -> pubDate = LocalDateTime.parse(
|
||||
"pubDate" -> pubDate = ZonedDateTime.parse(
|
||||
node.textContent,
|
||||
DateTimeFormatter.RFC_1123_DATE_TIME
|
||||
)
|
||||
|
@ -112,6 +129,7 @@ data class Article(
|
|||
}
|
||||
}
|
||||
}
|
||||
Log.e(null, "$title ${pubDate.toString()}")
|
||||
if (imageURL == null && url != null) {
|
||||
runBlocking(IO) {
|
||||
val con = Jsoup.connect(url)
|
||||
|
@ -128,8 +146,16 @@ data class Article(
|
|||
}
|
||||
}
|
||||
}
|
||||
return Article(null,title, url, description, imageURL, author, pubDate?.toEpochSecond(
|
||||
ZoneOffset.UTC), feed)
|
||||
return Article(
|
||||
null,
|
||||
title,
|
||||
url,
|
||||
description,
|
||||
imageURL,
|
||||
author,
|
||||
pubDate?.toEpochSecond(),
|
||||
feed
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.lifecycle.LiveData
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.Article
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.Feed
|
||||
import eu.toldi.balazs.anotherfeedreader.sqlite.FeedsAndArticles
|
||||
import eu.toldi.balazs.anotherfeedreader.sqlite.FeedsAndArticlesDAO
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
|
@ -24,11 +23,29 @@ class FeedRepository(private val feedsAndArticleDAO: FeedsAndArticlesDAO) {
|
|||
|
||||
private val _isRefereshing: MutableState<Boolean> = mutableStateOf(false)
|
||||
|
||||
public val isRefeshing: State<Boolean>
|
||||
val isRefeshing: State<Boolean>
|
||||
get() = _isRefereshing
|
||||
|
||||
fun getAllArticles(): LiveData<List<FeedsAndArticles>> {
|
||||
return feedsAndArticleDAO.getAll()
|
||||
private var limit: Feed? = null
|
||||
|
||||
private val _articleState: MutableState<List<Article>> = mutableStateOf(emptyList())
|
||||
|
||||
val articleState: State<List<Article>> = _articleState
|
||||
|
||||
suspend fun initArticles() = withContext(IO) {
|
||||
_articleState.value = getAllArticles()
|
||||
}
|
||||
|
||||
suspend fun getAllArticles(): List<Article> {
|
||||
|
||||
return if (limit != null) {
|
||||
_isRefereshing.value = true
|
||||
withContext(IO) {
|
||||
feedsAndArticleDAO.getArticlesByFeed(limit!!.feedId!!).also {
|
||||
_isRefereshing.value = false
|
||||
}
|
||||
}
|
||||
} else withContext(IO) { feedsAndArticleDAO.getAllList() }
|
||||
}
|
||||
|
||||
suspend fun instert(feed: Feed, article: Article) {
|
||||
|
@ -42,9 +59,14 @@ class FeedRepository(private val feedsAndArticleDAO: FeedsAndArticlesDAO) {
|
|||
withContext(IO) {
|
||||
try {
|
||||
_isRefereshing.value = true
|
||||
val feeds = feedsAndArticleDAO.getFeedsList()
|
||||
feeds?.forEach { feed ->
|
||||
updateFeed(feed)
|
||||
if (limit != null) {
|
||||
updateFeed(limit!!)
|
||||
|
||||
} else {
|
||||
val feeds = feedsAndArticleDAO.getFeedsList()
|
||||
feeds?.forEach { feed ->
|
||||
updateFeed(feed)
|
||||
}
|
||||
}
|
||||
_isRefereshing.value = false
|
||||
} catch (e: Exception) {
|
||||
|
@ -71,7 +93,9 @@ class FeedRepository(private val feedsAndArticleDAO: FeedsAndArticlesDAO) {
|
|||
for (i in 0 until items.length) {
|
||||
val article: Node = items.item(i)
|
||||
val a = withContext(Dispatchers.IO) { Article.createFromNode(article, feed = feed) }
|
||||
feedsAndArticleDAO.insertArticle(feed, a)
|
||||
val a1 = withContext(IO) { a.url?.let { feedsAndArticleDAO.getArticleByURL(it) } }
|
||||
if (a != a1)
|
||||
feedsAndArticleDAO.insertArticle(feed, a)
|
||||
}
|
||||
|
||||
feed.faviconURL = withContext(Dispatchers.IO) {
|
||||
|
@ -89,4 +113,9 @@ class FeedRepository(private val feedsAndArticleDAO: FeedsAndArticlesDAO) {
|
|||
feedsAndArticleDAO.insertFeed(feed)
|
||||
updateFeeds()
|
||||
}
|
||||
|
||||
suspend fun setLimit(feed: Feed?) {
|
||||
limit = feed
|
||||
_articleState.value = getAllArticles()
|
||||
}
|
||||
}
|
|
@ -9,15 +9,24 @@ import eu.toldi.balazs.anotherfeedreader.entities.Feed
|
|||
interface FeedsAndArticlesDAO {
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM feed INNER JOIN article ON feed.feedId = article.containingFeedId ORDER BY article.pubDate")
|
||||
@Query("SELECT * FROM feed INNER JOIN article ON feed.feedId = article.containingFeedId ORDER BY article.pubDate DESC")
|
||||
fun getAll(): LiveData<List<FeedsAndArticles>>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM feed INNER JOIN article ON feed.feedId = article.containingFeedId ORDER BY article.pubDate DESC")
|
||||
fun getAllList(): List<Article>
|
||||
|
||||
@Query("SELECT * FROM feed")
|
||||
fun getFeeds(): LiveData<List<Feed>>
|
||||
|
||||
@Query("SELECT * FROM feed")
|
||||
fun getFeedsList(): List<Feed>
|
||||
|
||||
@Query("SELECT * FROM article WHERE url = :url")
|
||||
fun getArticleByURL(url: String): Article
|
||||
|
||||
@Query("SELECT * FROM feed INNER JOIN article ON feed.feedId = article.containingFeedId WHERE feed.feedId = :feedId ORDER BY article.pubDate DESC")
|
||||
fun getArticlesByFeed(feedId: Long): List<Article>
|
||||
|
||||
fun insertArticle(feed: Feed, article: Article) {
|
||||
feed.feedId?.let { article.feedId = it }
|
||||
|
|
|
@ -5,10 +5,10 @@ import androidx.compose.runtime.State
|
|||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.Article
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.Feed
|
||||
import eu.toldi.balazs.anotherfeedreader.repository.FeedRepository
|
||||
import eu.toldi.balazs.anotherfeedreader.sqlite.AppDatabase
|
||||
import eu.toldi.balazs.anotherfeedreader.sqlite.FeedsAndArticles
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -16,15 +16,19 @@ import kotlinx.coroutines.launch
|
|||
class FeedViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
private val feedRepo: FeedRepository
|
||||
val articles: LiveData<List<FeedsAndArticles>>
|
||||
val feedsState: LiveData<List<Feed>>
|
||||
val articles: State<List<Article>>
|
||||
val feeds: LiveData<List<Feed>>
|
||||
val isRefereshing: State<Boolean>
|
||||
|
||||
init {
|
||||
val feedsAndArticlesDao = AppDatabase.getInstance(application).feedsAndArticlesDao()
|
||||
feedRepo = FeedRepository(feedsAndArticleDAO = feedsAndArticlesDao)
|
||||
articles = feedRepo.getAllArticles()
|
||||
feedsState = feedRepo.getFeedList()
|
||||
articles = feedRepo.articleState
|
||||
viewModelScope.launch(IO) {
|
||||
//feedRepo.initArticles()
|
||||
feedRepo.updateFeeds()
|
||||
}
|
||||
feeds = feedRepo.getFeedList()
|
||||
isRefereshing = feedRepo.isRefeshing
|
||||
}
|
||||
|
||||
|
@ -36,12 +40,19 @@ class FeedViewModel(application: Application) : AndroidViewModel(application) {
|
|||
|
||||
fun changeFeed() {
|
||||
viewModelScope.launch {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun changeFeedList() {
|
||||
viewModelScope.launch {
|
||||
//feedsState.value = feedRepo.getFeedList().value
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun setLimit(feed: Feed?) {
|
||||
viewModelScope.launch {
|
||||
feedRepo.setLimit(feed)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ buildscript {
|
|||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:7.0.2"
|
||||
classpath 'com.android.tools.build:gradle:7.0.3'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
|
Loading…
Reference in a new issue