Big progress on persistent data storage
All checks were successful
continuous-integration/drone/push Build is passing

Signed-off-by: Balazs Toldi <balazs@toldi.eu>
This commit is contained in:
Balazs Toldi 2021-10-23 23:54:47 +02:00
parent 90da45609f
commit a4b3e7e80a
Signed by: Bazsalanszky
GPG key ID: 933820884952BE27
10 changed files with 147 additions and 174 deletions

View file

@ -26,6 +26,10 @@
<entry key="../../../../../layout/compose-model-1633889971370.xml" value="0.5796296296296296" /> <entry key="../../../../../layout/compose-model-1633889971370.xml" value="0.5796296296296296" />
<entry key="../../../../../layout/compose-model-1633894374457.xml" value="0.575925925925926" /> <entry key="../../../../../layout/compose-model-1633894374457.xml" value="0.575925925925926" />
<entry key="../../../../../layout/compose-model-1633894712445.xml" value="0.5" /> <entry key="../../../../../layout/compose-model-1633894712445.xml" value="0.5" />
<entry key="../../../../../layout/compose-model-1634049545704.xml" value="0.5722222222222222" />
<entry key="../../../../../layout/compose-model-1634049671897.xml" value="0.2777777777777778" />
<entry key="../../../../../layout/compose-model-1634139145792.xml" value="0.8684834123222749" />
<entry key="../../../../../layout/compose-model-1635019781952.xml" value="0.5435185185185185" />
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" value="0.21458333333333332" /> <entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" value="0.21458333333333332" />
</map> </map>
</option> </option>

View file

@ -29,7 +29,6 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import coil.compose.rememberImagePainter import coil.compose.rememberImagePainter
@ -38,16 +37,10 @@ import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import eu.toldi.balazs.anotherfeedreader.entities.Article import eu.toldi.balazs.anotherfeedreader.entities.Article
import eu.toldi.balazs.anotherfeedreader.entities.Feed import eu.toldi.balazs.anotherfeedreader.entities.Feed
import eu.toldi.balazs.anotherfeedreader.entities.FeedGroup import eu.toldi.balazs.anotherfeedreader.entities.FeedGroup
import eu.toldi.balazs.anotherfeedreader.sqlite.Repo
import eu.toldi.balazs.anotherfeedreader.ui.theme.AnotherFeedReaderTheme import eu.toldi.balazs.anotherfeedreader.ui.theme.AnotherFeedReaderTheme
import eu.toldi.balazs.anotherfeedreader.viewmodel.FeedViewModel import eu.toldi.balazs.anotherfeedreader.viewmodel.FeedViewModel
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.net.URL
import java.time.LocalDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle import java.time.format.FormatStyle
@ -104,8 +97,11 @@ class MainActivity : ComponentActivity() {
) { ) {
items(articleList.value.size) { index -> items(articleList.value.size) { index ->
val article = articleList.value[index] val articleAndFeed = articleList.value[index]
showArticle(article = article) articleAndFeed.articles.sortedByDescending { it.pubDate }
.forEach {
showArticle(articleAndFeed.feed, article = it)
}
} }
} }
} }
@ -150,7 +146,7 @@ class MainActivity : ComponentActivity() {
) { ) {
Text( Text(
text = if (selectedIndex == 0) "None" else viewModel.feedsState.value.filterIsInstance<FeedGroup>()[selectedIndex - 1].name.toString(), text = if (selectedIndex == 0) "None" else viewModel.feedsState.value!!.filterIsInstance<FeedGroup>()[selectedIndex - 1].name.toString(),
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable(onClick = { expanded = true }) .clickable(onClick = { expanded = true })
@ -166,13 +162,14 @@ class MainActivity : ComponentActivity() {
}) { }) {
Text(text = "None") Text(text = "None")
} }
viewModel.feedsState.value.filterIsInstance<FeedGroup>()
viewModel.feedsState.value!!.filterIsInstance<FeedGroup>()
.forEachIndexed { index, s -> .forEachIndexed { index, s ->
DropdownMenuItem(onClick = { DropdownMenuItem(onClick = {
selectedIndex = index + 1 selectedIndex = index + 1
expanded = false expanded = false
}) { }) {
viewModel.feedsState.value[index].name?.let { viewModel.feedsState.value!![index].name?.let {
Text(text = it) Text(text = it)
} }
} }
@ -196,12 +193,11 @@ class MainActivity : ComponentActivity() {
onClick = { onClick = {
try { try {
val feed = Feed(null,text) val feed = Feed(null,text)
syncJob = GlobalScope.launch(IO) { feed.updateFeed() }
if (selectedIndex == 0) if (selectedIndex == 0)
viewModel.addFeed(feed) viewModel.addFeed(feed)
else { else {
var counter = 0 var counter = 0
viewModel.feedsState.value.forEachIndexed { index, feedGroup -> viewModel.feedsState.value!!.forEachIndexed { index, feedGroup ->
if (feedGroup is FeedGroup) { if (feedGroup is FeedGroup) {
counter++ counter++
if (counter == selectedIndex) { if (counter == selectedIndex) {
@ -260,12 +256,23 @@ class MainActivity : ComponentActivity() {
} }
@Composable @Composable
fun showArticle(article: Article) { fun showArticle(feed: Feed, article: Article) {
val haptic = LocalHapticFeedback.current val haptic = LocalHapticFeedback.current
Card( Card(
Modifier Modifier
.pointerInput(Unit) { .pointerInput(Unit) {
detectTapGestures( detectTapGestures(
onTap = {
WebView.webURL = article.url.toString()
WebView.barTitle =
feed.name ?: ""
startActivity(
Intent(
baseContext,
WebView::class.java
)
)
},
onLongPress = { onLongPress = {
val share = val share =
Intent.createChooser(Intent().apply { Intent.createChooser(Intent().apply {
@ -290,20 +297,7 @@ class MainActivity : ComponentActivity() {
top = 2.dp, top = 2.dp,
bottom = 6.dp, bottom = 6.dp,
end = 6.dp end = 6.dp
) ),
.clickable {
WebView.webURL = article.url.toString()
WebView.barTitle =
article.feed?.name.toString() ?: ""
startActivity(
Intent(
baseContext,
WebView::class.java
)
)
},
elevation = 10.dp, elevation = 10.dp,
backgroundColor = MaterialTheme.colors.surface, backgroundColor = MaterialTheme.colors.surface,
shape = RoundedCornerShape(10.dp) shape = RoundedCornerShape(10.dp)
@ -314,8 +308,8 @@ class MainActivity : ComponentActivity() {
horizontalArrangement = Arrangement.Start, horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
val painter = if (article.feed?.faviconURL != null) { val painter = if (feed.faviconURL != null) {
rememberImagePainter(article.feed.faviconURL) rememberImagePainter(feed.faviconURL)
} else { } else {
painterResource(id = R.drawable.ic_launcher_background) painterResource(id = R.drawable.ic_launcher_background)
} }
@ -337,7 +331,7 @@ class MainActivity : ComponentActivity() {
modifier = Modifier.padding(horizontal = 2.dp) modifier = Modifier.padding(horizontal = 2.dp)
) )
} }
article.feed?.name?.let { feed.name?.let {
Text( Text(
text = (fun(): String { text = (fun(): String {
return if (it.length > 25) return if (it.length > 25)
@ -476,7 +470,7 @@ class MainActivity : ComponentActivity() {
drawerState: DrawerState, drawerState: DrawerState,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
val feedList = viewModel.feedsState.value val feedList = viewModel.feedsState?.value ?: emptyList()
ModalDrawer(drawerState = drawerState, drawerContent = { ModalDrawer(drawerState = drawerState, drawerContent = {
LazyColumn(modifier = Modifier.fillMaxHeight()) { LazyColumn(modifier = Modifier.fillMaxHeight()) {
item { item {
@ -507,7 +501,7 @@ class MainActivity : ComponentActivity() {
}, content = content) }, content = content)
} }
/*
@Preview(showBackground = true) @Preview(showBackground = true)
@Composable @Composable
fun DefaultPreview() { fun DefaultPreview() {
@ -539,7 +533,7 @@ class MainActivity : ComponentActivity() {
} }
} }
} }*/
} }

View file

@ -5,8 +5,6 @@ import androidx.room.Entity
import androidx.room.Ignore import androidx.room.Ignore
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.w3c.dom.Element import org.w3c.dom.Element
@ -74,7 +72,7 @@ data class Article(
} }
@ColumnInfo(name = "feedId") @ColumnInfo(name = "containingFeedId")
var feedId: Long = 0 var feedId: Long = 0
companion object { companion object {
/** /**

View file

@ -1,23 +1,12 @@
package eu.toldi.balazs.anotherfeedreader.entities package eu.toldi.balazs.anotherfeedreader.entities
import android.util.Log
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.Ignore import androidx.room.Ignore
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import eu.toldi.balazs.anotherfeedreader.MainActivity
import eu.toldi.balazs.anotherfeedreader.sqlite.Repo
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.IO
import net.mm2d.touchicon.TouchIconExtractor
import org.w3c.dom.Document
import org.w3c.dom.Node
import org.w3c.dom.NodeList
import java.net.URL import java.net.URL
import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory
@Entity @Entity(tableName = "feed")
open class Feed open class Feed
( (
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
@ -34,7 +23,7 @@ open class Feed
/** /**
* A hírcsatorna neve * A hírcsatorna neve
*/ */
@ColumnInfo(name = "title") @ColumnInfo(name = "name")
var name: String? = null var name: String? = null
@Ignore @Ignore
val link = URL(feedURL) val link = URL(feedURL)
@ -46,6 +35,7 @@ open class Feed
/** /**
* A hírcsatornához tartozó cikkek listája * A hírcsatornához tartozó cikkek listája
*/ */
@Ignore
open var articleList: List<Article> = emptyList() open var articleList: List<Article> = emptyList()
/** /**
@ -64,42 +54,6 @@ open class Feed
return articleList[i] return articleList[i]
} }
/**
* Hírcsatorna frissítése.
* Lekéri a hírcsatorna elérési útvonalán található összes új cikket és hozzáadja a cikkek listájához.
*
*/
open suspend fun updateFeed() {
val factory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance()
val builder: DocumentBuilder = factory.newDocumentBuilder()
var doc: Document = withContext(Dispatchers.IO) {
try {
builder.parse(link.toString())
} catch (e: Exception) {
Log.e(null, e.stackTraceToString())
builder.newDocument()
}
}
if (doc.getElementsByTagName("title").length > 0)
name = doc.getElementsByTagName("title").item(0).getTextContent()
val items: NodeList = doc.getElementsByTagName("item")
for (i in 0 until items.length) {
val article: Node = items.item(i)
val a = withContext(Dispatchers.IO) {Article.createFromNode(article, this@Feed)}
addArticle(a)
}
articleList.sortedByDescending { it.pubDate }
faviconURL = withContext(Dispatchers.IO) {
val extractor = TouchIconExtractor()
val icons = extractor.fromPage("https://" + link.host)
if (icons.size > 0)
icons.maxByOrNull { it.inferArea() }?.url
else null
}
Log.e(null, faviconURL.toString())
}
/** /**
* Megnézi hogy egy magadott cikk megtalálható-e a hírcsatornán * Megnézi hogy egy magadott cikk megtalálható-e a hírcsatornán
* @param a a keresett cikk * @param a a keresett cikk

View file

@ -1,6 +1,5 @@
package eu.toldi.balazs.anotherfeedreader.entities package eu.toldi.balazs.anotherfeedreader.entities
import java.io.IOException
import java.util.* import java.util.*
@ -45,24 +44,6 @@ open class FeedGroup(name: String) : Feed(name) {
return false return false
} }
/**
* @see Feed.updateFeed
* @throws IOException
*/
override suspend fun updateFeed() {
if (feedList == null) feedList = ArrayList()
if (articleList == null) articleList = ArrayList()
feedList.forEach { f ->
f.updateFeed()
f.articleList.forEach {
if (!hasAricle(it))
articleList += it
}
}
articleList = articleList.sortedByDescending { it.pubDate }
}
/** /**
* @see Feed.getArticleCount * @see Feed.getArticleCount
* @return * @return

View file

@ -1,58 +1,92 @@
package eu.toldi.balazs.anotherfeedreader.repository package eu.toldi.balazs.anotherfeedreader.repository
import android.util.Log
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import eu.toldi.balazs.anotherfeedreader.entities.Article import eu.toldi.balazs.anotherfeedreader.entities.Article
import eu.toldi.balazs.anotherfeedreader.entities.Feed import eu.toldi.balazs.anotherfeedreader.entities.Feed
import eu.toldi.balazs.anotherfeedreader.entities.FeedData import eu.toldi.balazs.anotherfeedreader.sqlite.FeedsAndArticles
import eu.toldi.balazs.anotherfeedreader.entities.FeedGroup import eu.toldi.balazs.anotherfeedreader.sqlite.FeedsAndArticlesDAO
import eu.toldi.balazs.anotherfeedreader.sqlite.ArticleDAO import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.net.URL import net.mm2d.touchicon.TouchIconExtractor
import org.w3c.dom.Document
import org.w3c.dom.Node
import org.w3c.dom.NodeList
import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory
class FeedRepository(private val articleDAO: ArticleDAO) { class FeedRepository(private val feedsAndArticleDAO: FeedsAndArticlesDAO) {
val feedData = FeedData().apply {
addFeed(FeedGroup("magyar").apply { private val _isRefereshing: MutableState<Boolean> = mutableStateOf(false)
addFeed(Feed(feedURL = "https://hvg.hu/rss"))
addFeed(Feed(feedURL = "https://telex.hu/rss")) public val isRefeshing: State<Boolean>
addFeed(Feed(feedURL = "https://24.hu/feed")) get() = _isRefereshing
addFeed(Feed(feedURL = "https://444.hu/feed"))
}) fun getAllArticles(): LiveData<List<FeedsAndArticles>> {
addFeed(Feed(feedURL = "https://www.theguardian.com/world/rss")) return feedsAndArticleDAO.getAll()
} }
fun getAllArticles(): LiveData<List<Article>> { suspend fun instert(feed: Feed, article: Article) {
return articleDAO.getAll() feedsAndArticleDAO.insertArticle(feed, article)
} }
suspend fun instert(article: Article){
articleDAO.insertAll(article)
}
suspend fun delete(article: Article){ fun getFeedList(): LiveData<List<Feed>> = feedsAndArticleDAO.getFeeds()
articleDAO.delete(article)
}
fun getFeedList() : List<Feed> = feedData.feedList
suspend fun updateFeeds() { suspend fun updateFeeds() {
var done = false withContext(IO) {
feedData.limit?.let { try {
it.updateFeed() _isRefereshing.value = true
done = true val feeds = feedsAndArticleDAO.getFeedsList()
} feeds?.forEach { feed ->
if (!done) updateFeed(feed)
feedData.updateFeed() }
_isRefereshing.value = false
} catch (e: Exception) {
feedData.articleList.forEach { }
withContext(IO){
instert(it) }
}
private suspend fun updateFeed(feed: Feed) {
val factory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance()
val builder: DocumentBuilder = factory.newDocumentBuilder()
var doc: Document = withContext(Dispatchers.IO) {
try {
builder.parse(feed.link.toString())
} catch (e: Exception) {
Log.e(null, e.stackTraceToString())
builder.newDocument()
} }
} }
if (doc.getElementsByTagName("title").length > 0)
feed.name = doc.getElementsByTagName("title").item(0).getTextContent()
val items: NodeList = doc.getElementsByTagName("item")
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)
}
feed.faviconURL = withContext(Dispatchers.IO) {
val extractor = TouchIconExtractor()
val icons = extractor.fromPage("https://" + feed.link.host)
if (icons.size > 0)
icons.maxByOrNull { it.inferArea() }?.url
else null
}
feedsAndArticleDAO.updateFeed(feed)
} }
fun addFeed(feed: Feed) {
feedData.addFeed(feed) suspend fun addFeed(feed: Feed) {
feedsAndArticleDAO.insertFeed(feed)
updateFeeds()
} }
} }

View file

@ -5,10 +5,12 @@ import androidx.room.Database
import androidx.room.Room import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import eu.toldi.balazs.anotherfeedreader.entities.Article import eu.toldi.balazs.anotherfeedreader.entities.Article
import eu.toldi.balazs.anotherfeedreader.entities.Feed
@Database(entities = [Article::class], version = 1) @Database(entities = [Article::class, Feed::class], version = 4)
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
abstract fun articleDao(): ArticleDAO abstract fun articleDao(): ArticleDAO
abstract fun feedsAndArticlesDao(): FeedsAndArticlesDAO
companion object { companion object {
@Volatile @Volatile

View file

@ -9,7 +9,7 @@ data class FeedsAndArticles(
@Embedded val feed: Feed, @Embedded val feed: Feed,
@Relation( @Relation(
parentColumn = "feedId", parentColumn = "feedId",
entityColumn = "feedId" entityColumn = "containingFeedId"
) )
val articles: List<Article> val articles: List<Article>
) )

View file

@ -3,22 +3,35 @@ package eu.toldi.balazs.anotherfeedreader.sqlite
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.room.* import androidx.room.*
import eu.toldi.balazs.anotherfeedreader.entities.Article import eu.toldi.balazs.anotherfeedreader.entities.Article
import eu.toldi.balazs.anotherfeedreader.entities.Feed
@Dao @Dao
interface FeedsAndArticlesDAO { interface FeedsAndArticlesDAO {
@Transaction @Transaction
@Query("SELECT * FROM feed") @Query("SELECT * FROM feed INNER JOIN article ON feed.feedId = article.containingFeedId ORDER BY article.pubDate")
fun getAll(): LiveData<List<FeedsAndArticles>> fun getAll(): LiveData<List<FeedsAndArticles>>
@Query("SELECT * FROM article WHERE articleId IN (:articleIds)") @Query("SELECT * FROM feed")
fun loadAllByIds(articleIds: IntArray): List<Article> fun getFeeds(): LiveData<List<Feed>>
@Query("SELECT * FROM article WHERE url=:url") @Query("SELECT * FROM feed")
fun findArticleByURL(url: String): Article? fun getFeedsList(): List<Feed>
fun insertArticle(feed: Feed, article: Article) {
feed.feedId?.let { article.feedId = it }
_insertArticles(listOf(article))
}
@Insert @Insert
fun insertArticles(vararg articles: Article) fun _insertArticles(articles: List<Article>)
@Update
fun updateFeed(feed: Feed)
@Insert
fun insertFeed(feed: Feed)
@Delete @Delete
fun deleteArticle(article: Article) fun deleteArticle(article: Article)

View file

@ -1,62 +1,55 @@
package eu.toldi.balazs.anotherfeedreader.viewmodel package eu.toldi.balazs.anotherfeedreader.viewmodel
import android.app.Application import android.app.Application
import androidx.compose.runtime.MutableState import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import eu.toldi.balazs.anotherfeedreader.entities.Article
import eu.toldi.balazs.anotherfeedreader.entities.Feed import eu.toldi.balazs.anotherfeedreader.entities.Feed
import eu.toldi.balazs.anotherfeedreader.entities.FeedData
import eu.toldi.balazs.anotherfeedreader.entities.FeedGroup
import eu.toldi.balazs.anotherfeedreader.repository.FeedRepository import eu.toldi.balazs.anotherfeedreader.repository.FeedRepository
import eu.toldi.balazs.anotherfeedreader.sqlite.AppDatabase 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.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.net.URL
class FeedViewModel(application: Application) : AndroidViewModel(application) { class FeedViewModel(application: Application) : AndroidViewModel(application) {
private val feedRepo: FeedRepository private val feedRepo: FeedRepository
val articles: LiveData<List<Article>> val articles: LiveData<List<FeedsAndArticles>>
val feedsState = mutableStateOf(emptyList<Feed>()) val feedsState: LiveData<List<Feed>>
val isRefereshing: MutableState<Boolean> = mutableStateOf(false) val isRefereshing: State<Boolean>
init { init {
val articleDAO = AppDatabase.getInstance(application).articleDao() val feedsAndArticlesDao = AppDatabase.getInstance(application).feedsAndArticlesDao()
feedRepo = FeedRepository(articleDAO = articleDAO) feedRepo = FeedRepository(feedsAndArticleDAO = feedsAndArticlesDao)
articles = feedRepo.getAllArticles() articles = feedRepo.getAllArticles()
viewModelScope.launch { feedsState = feedRepo.getFeedList()
feedsState.value = feedRepo.getFeedList() isRefereshing = feedRepo.isRefeshing
}
} }
fun updateFeeds(): Job { fun updateFeeds(): Job {
return viewModelScope.launch { return viewModelScope.launch(IO) {
isRefereshing.value = true
feedRepo.updateFeeds() feedRepo.updateFeeds()
isRefereshing.value = false
} }
} }
fun changeFeed() { fun changeFeed() {
viewModelScope.launch { viewModelScope.launch {
isRefereshing.value = true
///articles.value = feedRepo.getFeedList()
isRefereshing.value = false
} }
} }
fun changeFeedList() { fun changeFeedList() {
viewModelScope.launch { viewModelScope.launch {
feedsState.value = feedRepo.getFeedList() //feedsState.value = feedRepo.getFeedList().value
} }
} }
fun addFeed(feed: Feed){ fun addFeed(feed: Feed) {
feedRepo.addFeed(feed) viewModelScope.launch(IO) {
feedsState.value = feedRepo.getFeedList() feedRepo.addFeed(feed)
}
} }
} }