Refactor repository coroutine
This commit is contained in:
parent
004005d8be
commit
e1e43019f5
|
@ -5,10 +5,11 @@ import androidx.hilt.work.HiltWorkerFactory
|
|||
import androidx.work.Configuration
|
||||
import androidx.work.WorkManager
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import me.ash.reader.data.module.ApplicationScope
|
||||
import me.ash.reader.data.module.DispatcherDefault
|
||||
import me.ash.reader.data.repository.*
|
||||
import me.ash.reader.data.source.OpmlLocalDataSource
|
||||
import me.ash.reader.data.source.ReaderDatabase
|
||||
|
@ -57,9 +58,13 @@ class App : Application(), Configuration.Provider {
|
|||
@ApplicationScope
|
||||
lateinit var applicationScope: CoroutineScope
|
||||
|
||||
@Inject
|
||||
@DispatcherDefault
|
||||
lateinit var dispatcherDefault: CoroutineDispatcher
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
applicationScope.launch(Dispatchers.IO) {
|
||||
applicationScope.launch(dispatcherDefault) {
|
||||
accountInit()
|
||||
workerInit()
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ import java.io.IOException
|
|||
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
||||
val Context.currentAccountId: Int
|
||||
get() = this.dataStore.get(DataStoreKeys.CurrentAccountId)!!
|
||||
val Context.currentAccountType: Int
|
||||
get() = this.dataStore.get(DataStoreKeys.CurrentAccountType)!!
|
||||
|
||||
suspend fun <T> DataStore<Preferences>.put(dataStoreKeys: DataStoreKeys<T>, value: T) {
|
||||
this.edit {
|
||||
|
|
|
@ -7,7 +7,7 @@ import androidx.paging.PagingSource
|
|||
import androidx.work.*
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import me.ash.reader.currentAccountId
|
||||
|
@ -32,6 +32,7 @@ abstract class AbstractRssRepository constructor(
|
|||
private val feedDao: FeedDao,
|
||||
private val rssNetworkDataSource: RssNetworkDataSource,
|
||||
private val workManager: WorkManager,
|
||||
private val dispatcherIO: CoroutineDispatcher,
|
||||
) {
|
||||
data class SyncState(
|
||||
val feedCount: Int = 0,
|
||||
|
@ -59,11 +60,11 @@ abstract class AbstractRssRepository constructor(
|
|||
}
|
||||
|
||||
fun pullGroups(): Flow<MutableList<Group>> {
|
||||
return groupDao.queryAllGroup(context.currentAccountId).flowOn(Dispatchers.IO)
|
||||
return groupDao.queryAllGroup(context.currentAccountId).flowOn(dispatcherIO)
|
||||
}
|
||||
|
||||
fun pullFeeds(): Flow<MutableList<GroupWithFeed>> {
|
||||
return groupDao.queryAllGroupWithFeedAsFlow(context.currentAccountId).flowOn(Dispatchers.IO)
|
||||
return groupDao.queryAllGroupWithFeedAsFlow(context.currentAccountId).flowOn(dispatcherIO)
|
||||
}
|
||||
|
||||
fun pullArticles(
|
||||
|
@ -72,7 +73,6 @@ abstract class AbstractRssRepository constructor(
|
|||
isStarred: Boolean = false,
|
||||
isUnread: Boolean = false,
|
||||
): PagingSource<Int, ArticleWithFeed> {
|
||||
Log.i("RLog", "thread:pullArticles ${Thread.currentThread().name}")
|
||||
val accountId = context.currentAccountId
|
||||
Log.i(
|
||||
"RLog",
|
||||
|
@ -107,7 +107,6 @@ abstract class AbstractRssRepository constructor(
|
|||
isStarred: Boolean = false,
|
||||
isUnread: Boolean = false,
|
||||
): Flow<List<ImportantCount>> {
|
||||
Log.i("RLog", "thread:pullImportant ${Thread.currentThread().name}")
|
||||
val accountId = context.currentAccountId
|
||||
Log.i(
|
||||
"RLog",
|
||||
|
@ -119,7 +118,7 @@ abstract class AbstractRssRepository constructor(
|
|||
isUnread -> articleDao
|
||||
.queryImportantCountWhenIsUnread(accountId, isUnread)
|
||||
else -> articleDao.queryImportantCountWhenIsAll(accountId)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
}.flowOn(dispatcherIO)
|
||||
}
|
||||
|
||||
suspend fun findFeedById(id: String): Feed? {
|
||||
|
@ -130,7 +129,7 @@ abstract class AbstractRssRepository constructor(
|
|||
return articleDao.queryById(id)
|
||||
}
|
||||
|
||||
suspend fun isExist(url: String): Boolean {
|
||||
suspend fun isFeedExist(url: String): Boolean {
|
||||
return feedDao.queryByLink(context.currentAccountId, url).isNotEmpty()
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,11 @@ import android.content.Context
|
|||
import android.util.Log
|
||||
import androidx.work.WorkManager
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import me.ash.reader.*
|
||||
import me.ash.reader.currentAccountId
|
||||
import me.ash.reader.data.account.AccountDao
|
||||
import me.ash.reader.data.article.Article
|
||||
import me.ash.reader.data.article.ArticleDao
|
||||
|
@ -13,11 +16,16 @@ import me.ash.reader.data.feed.Feed
|
|||
import me.ash.reader.data.feed.FeedDao
|
||||
import me.ash.reader.data.group.Group
|
||||
import me.ash.reader.data.group.GroupDao
|
||||
import me.ash.reader.data.module.ApplicationScope
|
||||
import me.ash.reader.data.module.DispatcherDefault
|
||||
import me.ash.reader.data.module.DispatcherIO
|
||||
import me.ash.reader.data.source.FeverApiDataSource
|
||||
import me.ash.reader.data.source.RssNetworkDataSource
|
||||
import me.ash.reader.spacerDollar
|
||||
import net.dankito.readability4j.extended.Readability4JExtended
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.set
|
||||
|
||||
class FeverRssRepository @Inject constructor(
|
||||
@ApplicationContext
|
||||
|
@ -29,10 +37,17 @@ class FeverRssRepository @Inject constructor(
|
|||
private val feverApiDataSource: FeverApiDataSource,
|
||||
private val accountDao: AccountDao,
|
||||
rssNetworkDataSource: RssNetworkDataSource,
|
||||
@ApplicationScope
|
||||
private val applicationScope: CoroutineScope,
|
||||
@DispatcherDefault
|
||||
private val dispatcherDefault: CoroutineDispatcher,
|
||||
@DispatcherIO
|
||||
private val dispatcherIO: CoroutineDispatcher,
|
||||
workManager: WorkManager,
|
||||
) : AbstractRssRepository(
|
||||
context, accountDao, articleDao, groupDao,
|
||||
feedDao, rssNetworkDataSource, workManager,
|
||||
dispatcherIO
|
||||
) {
|
||||
override suspend fun updateArticleInfo(article: Article) {
|
||||
articleDao.update(article)
|
||||
|
@ -58,88 +73,90 @@ class FeverRssRepository @Inject constructor(
|
|||
}
|
||||
|
||||
override suspend fun sync() {
|
||||
mutex.withLock {
|
||||
val accountId = context.currentAccountId
|
||||
applicationScope.launch(dispatcherDefault) {
|
||||
mutex.withLock {
|
||||
val accountId = context.currentAccountId
|
||||
|
||||
updateSyncState {
|
||||
it.copy(
|
||||
feedCount = 1,
|
||||
syncedCount = 1,
|
||||
currentFeedName = "Fever"
|
||||
)
|
||||
}
|
||||
updateSyncState {
|
||||
it.copy(
|
||||
feedCount = 1,
|
||||
syncedCount = 1,
|
||||
currentFeedName = "Fever"
|
||||
)
|
||||
}
|
||||
|
||||
if (feedDao.queryAll(accountId).isNullOrEmpty()) {
|
||||
// Temporary add feeds
|
||||
val feverFeeds = feverApiDataSource.feeds().execute().body()!!.feeds
|
||||
val feverGroupsBody = feverApiDataSource.groups().execute().body()!!
|
||||
Log.i("RLog", "Fever groups: $feverGroupsBody")
|
||||
feverGroupsBody.groups.forEach {
|
||||
groupDao.insert(
|
||||
Group(
|
||||
if (feedDao.queryAll(accountId).isNullOrEmpty()) {
|
||||
// Temporary add feeds
|
||||
val feverFeeds = feverApiDataSource.feeds().execute().body()!!.feeds
|
||||
val feverGroupsBody = feverApiDataSource.groups().execute().body()!!
|
||||
Log.i("RLog", "Fever groups: $feverGroupsBody")
|
||||
feverGroupsBody.groups.forEach {
|
||||
groupDao.insert(
|
||||
Group(
|
||||
id = accountId.spacerDollar(it.id),
|
||||
name = it.title,
|
||||
accountId = accountId,
|
||||
)
|
||||
)
|
||||
}
|
||||
val feverFeedsGroupsMap = mutableMapOf<Int, Int>()
|
||||
feverGroupsBody.feeds_groups.forEach { item ->
|
||||
item.feed_ids
|
||||
.split(",")
|
||||
.map { it.toInt() }
|
||||
.forEach { id ->
|
||||
feverFeedsGroupsMap[id] = item.group_id
|
||||
}
|
||||
}
|
||||
val feeds = feverFeeds.map {
|
||||
Feed(
|
||||
id = accountId.spacerDollar(it.id),
|
||||
name = it.title,
|
||||
accountId = accountId,
|
||||
url = it.url,
|
||||
groupId = feverFeedsGroupsMap[it.id].toString(),
|
||||
accountId = accountId
|
||||
)
|
||||
)
|
||||
}
|
||||
feedDao.insertList(feeds)
|
||||
}
|
||||
val feverFeedsGroupsMap = mutableMapOf<Int, Int>()
|
||||
feverGroupsBody.feeds_groups.forEach { item ->
|
||||
item.feed_ids
|
||||
.split(",")
|
||||
.map { it.toInt() }
|
||||
.forEach { id ->
|
||||
feverFeedsGroupsMap[id] = item.group_id
|
||||
}
|
||||
}
|
||||
val feeds = feverFeeds.map {
|
||||
Feed(
|
||||
id = accountId.spacerDollar(it.id),
|
||||
name = it.title,
|
||||
url = it.url,
|
||||
groupId = feverFeedsGroupsMap[it.id].toString(),
|
||||
accountId = accountId
|
||||
)
|
||||
}
|
||||
feedDao.insertList(feeds)
|
||||
}
|
||||
|
||||
// Add articles
|
||||
val articles = mutableListOf<Article>()
|
||||
feverApiDataSource.itemsBySince(since = 1647444325925621L)
|
||||
.execute().body()!!.items
|
||||
.forEach {
|
||||
articles.add(
|
||||
Article(
|
||||
id = accountId.spacerDollar(it.id),
|
||||
date = Date(it.created_on_time * 1000),
|
||||
title = it.title,
|
||||
author = it.author,
|
||||
rawDescription = it.html,
|
||||
shortDescription = (
|
||||
Readability4JExtended("", it.html)
|
||||
.parse().textContent ?: ""
|
||||
).take(100).trim(),
|
||||
link = it.url,
|
||||
accountId = accountId,
|
||||
feedId = it.feed_id.toString(),
|
||||
isUnread = it.is_read == 0,
|
||||
isStarred = it.is_saved == 1,
|
||||
// Add articles
|
||||
val articles = mutableListOf<Article>()
|
||||
feverApiDataSource.itemsBySince(since = 1647444325925621L)
|
||||
.execute().body()!!.items
|
||||
.forEach {
|
||||
articles.add(
|
||||
Article(
|
||||
id = accountId.spacerDollar(it.id),
|
||||
date = Date(it.created_on_time * 1000),
|
||||
title = it.title,
|
||||
author = it.author,
|
||||
rawDescription = it.html,
|
||||
shortDescription = (
|
||||
Readability4JExtended("", it.html)
|
||||
.parse().textContent ?: ""
|
||||
).take(100).trim(),
|
||||
link = it.url,
|
||||
accountId = accountId,
|
||||
feedId = it.feed_id.toString(),
|
||||
isUnread = it.is_read == 0,
|
||||
isStarred = it.is_saved == 1,
|
||||
)
|
||||
)
|
||||
}
|
||||
articleDao.insertList(articles)
|
||||
|
||||
// Complete sync
|
||||
accountDao.update(accountDao.queryById(accountId)!!.apply {
|
||||
updateAt = Date()
|
||||
})
|
||||
updateSyncState {
|
||||
it.copy(
|
||||
feedCount = 0,
|
||||
syncedCount = 0,
|
||||
currentFeedName = ""
|
||||
)
|
||||
}
|
||||
articleDao.insertList(articles)
|
||||
|
||||
// Complete sync
|
||||
accountDao.update(accountDao.queryById(accountId)!!.apply {
|
||||
updateAt = Date()
|
||||
})
|
||||
updateSyncState {
|
||||
it.copy(
|
||||
feedCount = 0,
|
||||
syncedCount = 0,
|
||||
currentFeedName = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,7 @@ import androidx.core.app.NotificationCompat
|
|||
import androidx.core.content.ContextCompat.getSystemService
|
||||
import androidx.work.WorkManager
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.*
|
||||
import me.ash.reader.MainActivity
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.currentAccountId
|
||||
|
@ -26,6 +22,9 @@ import me.ash.reader.data.feed.Feed
|
|||
import me.ash.reader.data.feed.FeedDao
|
||||
import me.ash.reader.data.group.Group
|
||||
import me.ash.reader.data.group.GroupDao
|
||||
import me.ash.reader.data.module.ApplicationScope
|
||||
import me.ash.reader.data.module.DispatcherDefault
|
||||
import me.ash.reader.data.module.DispatcherIO
|
||||
import me.ash.reader.data.source.RssNetworkDataSource
|
||||
import me.ash.reader.ui.page.common.ExtraName
|
||||
import me.ash.reader.ui.page.common.NotificationGroupName
|
||||
|
@ -41,10 +40,17 @@ class LocalRssRepository @Inject constructor(
|
|||
private val rssNetworkDataSource: RssNetworkDataSource,
|
||||
private val accountDao: AccountDao,
|
||||
private val groupDao: GroupDao,
|
||||
@ApplicationScope
|
||||
private val applicationScope: CoroutineScope,
|
||||
@DispatcherDefault
|
||||
private val dispatcherDefault: CoroutineDispatcher,
|
||||
@DispatcherIO
|
||||
private val dispatcherIO: CoroutineDispatcher,
|
||||
workManager: WorkManager,
|
||||
) : AbstractRssRepository(
|
||||
context, accountDao, articleDao, groupDao,
|
||||
feedDao, rssNetworkDataSource, workManager,
|
||||
dispatcherIO
|
||||
) {
|
||||
private val notificationManager: NotificationManager =
|
||||
(getSystemService(
|
||||
|
@ -84,40 +90,38 @@ class LocalRssRepository @Inject constructor(
|
|||
}
|
||||
|
||||
override suspend fun sync() {
|
||||
mutex.withLock {
|
||||
withContext(Dispatchers.IO) {
|
||||
val preTime = System.currentTimeMillis()
|
||||
val accountId = context.currentAccountId
|
||||
val articles = mutableListOf<Article>()
|
||||
feedDao.queryAll(accountId)
|
||||
.also { feed -> updateSyncState { it.copy(feedCount = feed.size) } }
|
||||
.map { feed -> async { syncFeed(feed) } }
|
||||
.awaitAll()
|
||||
.forEach {
|
||||
if (it.isNotify) {
|
||||
notify(it.articles)
|
||||
}
|
||||
articles.addAll(it.articles)
|
||||
applicationScope.launch(dispatcherDefault) {
|
||||
val preTime = System.currentTimeMillis()
|
||||
val accountId = context.currentAccountId
|
||||
val articles = mutableListOf<Article>()
|
||||
feedDao.queryAll(accountId)
|
||||
.also { feed -> updateSyncState { it.copy(feedCount = feed.size) } }
|
||||
.map { feed -> async { syncFeed(feed) } }
|
||||
.awaitAll()
|
||||
.forEach {
|
||||
if (it.isNotify) {
|
||||
notify(it.articles)
|
||||
}
|
||||
articles.addAll(it.articles)
|
||||
}
|
||||
|
||||
articleDao.insertList(articles)
|
||||
Log.i("RlOG", "onCompletion: ${System.currentTimeMillis() - preTime}")
|
||||
accountDao.queryById(accountId)?.let { account ->
|
||||
accountDao.update(
|
||||
account.apply {
|
||||
updateAt = Date()
|
||||
}
|
||||
)
|
||||
}
|
||||
updateSyncState {
|
||||
it.copy(
|
||||
feedCount = 0,
|
||||
syncedCount = 0,
|
||||
currentFeedName = ""
|
||||
)
|
||||
}
|
||||
articleDao.insertList(articles)
|
||||
Log.i("RlOG", "onCompletion: ${System.currentTimeMillis() - preTime}")
|
||||
accountDao.queryById(accountId)?.let { account ->
|
||||
accountDao.update(
|
||||
account.apply {
|
||||
updateAt = Date()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
updateSyncState {
|
||||
it.copy(
|
||||
feedCount = 0,
|
||||
syncedCount = 0,
|
||||
currentFeedName = ""
|
||||
)
|
||||
}
|
||||
}.join()
|
||||
}
|
||||
|
||||
data class ArticleNotify(
|
||||
|
@ -127,10 +131,20 @@ class LocalRssRepository @Inject constructor(
|
|||
|
||||
private suspend fun syncFeed(feed: Feed): ArticleNotify {
|
||||
val latest = articleDao.queryLatestByFeedId(context.currentAccountId, feed.id)
|
||||
val articles = rssHelper.queryRssXml(feed, latest?.link).also {
|
||||
if (feed.icon == null && it.isNotEmpty()) {
|
||||
rssHelper.queryRssIcon(feedDao, feed, it.first().link)
|
||||
var articles: List<Article>? = null
|
||||
try {
|
||||
articles = rssHelper.queryRssXml(feed, latest?.link)
|
||||
} catch (e: Exception) {
|
||||
Log.e("RLog", "queryRssXml[${feed.name}]: ${e.message}")
|
||||
return ArticleNotify(listOf(), false)
|
||||
}
|
||||
try {
|
||||
if (feed.icon == null && !articles.isNullOrEmpty()) {
|
||||
rssHelper.queryRssIcon(feedDao, feed, articles.first().link)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("RLog", "queryRssIcon[${feed.name}]: ${e.message}")
|
||||
return ArticleNotify(listOf(), false)
|
||||
}
|
||||
updateSyncState {
|
||||
it.copy(
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
package me.ash.reader.data.repository
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import be.ceau.opml.OpmlWriter
|
||||
import be.ceau.opml.entity.Body
|
||||
import be.ceau.opml.entity.Head
|
||||
import be.ceau.opml.entity.Opml
|
||||
import be.ceau.opml.entity.Outline
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.currentAccountId
|
||||
import me.ash.reader.data.account.AccountDao
|
||||
import me.ash.reader.data.feed.Feed
|
||||
import me.ash.reader.data.feed.FeedDao
|
||||
import me.ash.reader.data.group.GroupDao
|
||||
import me.ash.reader.data.source.OpmlLocalDataSource
|
||||
import me.ash.reader.spacerDollar
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
@ -25,69 +26,70 @@ class OpmlRepository @Inject constructor(
|
|||
private val feedDao: FeedDao,
|
||||
private val accountDao: AccountDao,
|
||||
private val rssRepository: RssRepository,
|
||||
private val opmlLocalDataSource: OpmlLocalDataSource
|
||||
private val opmlLocalDataSource: OpmlLocalDataSource,
|
||||
private val stringsRepository: StringsRepository,
|
||||
) {
|
||||
@Throws(Exception::class)
|
||||
suspend fun saveToDatabase(inputStream: InputStream) {
|
||||
try {
|
||||
val defaultGroup = groupDao.queryById(opmlLocalDataSource.getDefaultGroupId())!!
|
||||
val groupWithFeedList =
|
||||
opmlLocalDataSource.parseFileInputStream(inputStream, defaultGroup)
|
||||
groupWithFeedList.forEach { groupWithFeed ->
|
||||
if (groupWithFeed.group != defaultGroup) {
|
||||
groupDao.insert(groupWithFeed.group)
|
||||
}
|
||||
val repeatList = mutableListOf<Feed>()
|
||||
groupWithFeed.feeds.forEach {
|
||||
it.groupId = groupWithFeed.group.id
|
||||
if (rssRepository.get().isExist(it.url)) {
|
||||
repeatList.add(it)
|
||||
}
|
||||
}
|
||||
feedDao.insertList((groupWithFeed.feeds subtract repeatList).toList())
|
||||
val defaultGroup = groupDao.queryById(getDefaultGroupId())!!
|
||||
val groupWithFeedList =
|
||||
opmlLocalDataSource.parseFileInputStream(inputStream, defaultGroup)
|
||||
groupWithFeedList.forEach { groupWithFeed ->
|
||||
if (groupWithFeed.group != defaultGroup) {
|
||||
groupDao.insert(groupWithFeed.group)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("saveToDatabase", "${e.message}")
|
||||
val repeatList = mutableListOf<Feed>()
|
||||
groupWithFeed.feeds.forEach {
|
||||
it.groupId = groupWithFeed.group.id
|
||||
if (rssRepository.get().isFeedExist(it.url)) {
|
||||
repeatList.add(it)
|
||||
}
|
||||
}
|
||||
feedDao.insertList((groupWithFeed.feeds subtract repeatList).toList())
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun saveToString(): String? =
|
||||
try {
|
||||
val defaultGroup = groupDao.queryById(opmlLocalDataSource.getDefaultGroupId())!!
|
||||
OpmlWriter().write(
|
||||
Opml(
|
||||
"2.0",
|
||||
Head(
|
||||
accountDao.queryById(context.currentAccountId).name,
|
||||
Date().toString(), null, null, null,
|
||||
null, null, null, null,
|
||||
null, null, null, null,
|
||||
),
|
||||
Body(groupDao.queryAllGroupWithFeed(context.currentAccountId).map {
|
||||
Outline(
|
||||
mapOf(
|
||||
"text" to it.group.name,
|
||||
"title" to it.group.name,
|
||||
"isDefault" to (it.group.id == defaultGroup.id).toString()
|
||||
),
|
||||
it.feeds.map { feed ->
|
||||
Outline(
|
||||
mapOf(
|
||||
"text" to feed.name,
|
||||
"title" to feed.name,
|
||||
"xmlUrl" to feed.url,
|
||||
"htmlUrl" to feed.url,
|
||||
"isNotification" to feed.isNotification.toString(),
|
||||
"isFullContent" to feed.isFullContent.toString(),
|
||||
),
|
||||
listOf()
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
)
|
||||
@Throws(Exception::class)
|
||||
suspend fun saveToString(): String {
|
||||
val defaultGroup = groupDao.queryById(getDefaultGroupId())!!
|
||||
return OpmlWriter().write(
|
||||
Opml(
|
||||
"2.0",
|
||||
Head(
|
||||
accountDao.queryById(context.currentAccountId).name,
|
||||
Date().toString(), null, null, null,
|
||||
null, null, null, null,
|
||||
null, null, null, null,
|
||||
),
|
||||
Body(groupDao.queryAllGroupWithFeed(context.currentAccountId).map {
|
||||
Outline(
|
||||
mapOf(
|
||||
"text" to it.group.name,
|
||||
"title" to it.group.name,
|
||||
"isDefault" to (it.group.id == defaultGroup.id).toString()
|
||||
),
|
||||
it.feeds.map { feed ->
|
||||
Outline(
|
||||
mapOf(
|
||||
"text" to feed.name,
|
||||
"title" to feed.name,
|
||||
"xmlUrl" to feed.url,
|
||||
"htmlUrl" to feed.url,
|
||||
"isNotification" to feed.isNotification.toString(),
|
||||
"isFullContent" to feed.isFullContent.toString(),
|
||||
),
|
||||
listOf()
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e("saveToString", "${e.message}")
|
||||
null
|
||||
}
|
||||
)!!
|
||||
}
|
||||
|
||||
private fun getDefaultGroupId(): String {
|
||||
val readYouString = stringsRepository.getString(R.string.read_you)
|
||||
val defaultString = stringsRepository.getString(R.string.defaults)
|
||||
return context.currentAccountId.spacerDollar(readYouString + defaultString)
|
||||
}
|
||||
}
|
|
@ -4,17 +4,20 @@ import android.content.Context
|
|||
import android.text.Html
|
||||
import android.util.Log
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.ash.reader.currentAccountId
|
||||
import me.ash.reader.data.article.Article
|
||||
import me.ash.reader.data.feed.Feed
|
||||
import me.ash.reader.data.feed.FeedDao
|
||||
import me.ash.reader.data.feed.FeedWithArticle
|
||||
import me.ash.reader.data.module.DispatcherIO
|
||||
import me.ash.reader.data.source.RssNetworkDataSource
|
||||
import me.ash.reader.spacerDollar
|
||||
import net.dankito.readability4j.Readability4J
|
||||
import net.dankito.readability4j.extended.Readability4JExtended
|
||||
import okhttp3.*
|
||||
import java.io.IOException
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import java.text.ParsePosition
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
@ -24,19 +27,23 @@ class RssHelper @Inject constructor(
|
|||
@ApplicationContext
|
||||
private val context: Context,
|
||||
private val rssNetworkDataSource: RssNetworkDataSource,
|
||||
@DispatcherIO
|
||||
private val dispatcherIO: CoroutineDispatcher,
|
||||
) {
|
||||
@Throws(Exception::class)
|
||||
suspend fun searchFeed(feedLink: String): FeedWithArticle {
|
||||
val accountId = context.currentAccountId
|
||||
val parseRss = rssNetworkDataSource.parseRss(feedLink)
|
||||
val feed = Feed(
|
||||
id = accountId.spacerDollar(UUID.randomUUID().toString()),
|
||||
name = parseRss.title!!,
|
||||
url = feedLink,
|
||||
groupId = "",
|
||||
accountId = accountId,
|
||||
)
|
||||
return FeedWithArticle(feed, queryRssXml(feed))
|
||||
return withContext(dispatcherIO) {
|
||||
val accountId = context.currentAccountId
|
||||
val parseRss = rssNetworkDataSource.parseRss(feedLink)
|
||||
val feed = Feed(
|
||||
id = accountId.spacerDollar(UUID.randomUUID().toString()),
|
||||
name = parseRss.title!!,
|
||||
url = feedLink,
|
||||
groupId = "",
|
||||
accountId = accountId,
|
||||
)
|
||||
FeedWithArticle(feed, queryRssXml(feed))
|
||||
}
|
||||
}
|
||||
|
||||
fun parseDescriptionContent(link: String, content: String): String {
|
||||
|
@ -46,42 +53,39 @@ class RssHelper @Inject constructor(
|
|||
return element.toString()
|
||||
}
|
||||
|
||||
fun parseFullContent(link: String, title: String, callback: (String) -> Unit) {
|
||||
OkHttpClient()
|
||||
.newCall(Request.Builder().url(link).build())
|
||||
.enqueue(object : Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
callback(e.message.toString())
|
||||
@Throws(Exception::class)
|
||||
suspend fun parseFullContent(link: String, title: String): String {
|
||||
return withContext(dispatcherIO) {
|
||||
val response = OkHttpClient()
|
||||
.newCall(Request.Builder().url(link).build())
|
||||
.execute()
|
||||
val content = response.body!!.string()
|
||||
val readability4J: Readability4J =
|
||||
Readability4JExtended(link, content)
|
||||
val articleContent = readability4J.parse().articleContent
|
||||
if (articleContent == null) {
|
||||
""
|
||||
} else {
|
||||
val h1Element = articleContent.selectFirst("h1")
|
||||
if (h1Element != null && h1Element.hasText() && h1Element.text() == title) {
|
||||
h1Element.remove()
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
val content = response.body?.string()
|
||||
val readability4J: Readability4J =
|
||||
Readability4JExtended(link, content ?: "")
|
||||
val articleContent = readability4J.parse().articleContent
|
||||
if (articleContent == null) {
|
||||
callback("")
|
||||
} else {
|
||||
val h1Element = articleContent.selectFirst("h1")
|
||||
if (h1Element != null && h1Element.hasText() && h1Element.text() == title) {
|
||||
h1Element.remove()
|
||||
}
|
||||
callback(articleContent.toString())
|
||||
}
|
||||
}
|
||||
})
|
||||
articleContent.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
suspend fun queryRssXml(
|
||||
feed: Feed,
|
||||
latestLink: String? = null,
|
||||
): List<Article> {
|
||||
val a = mutableListOf<Article>()
|
||||
try {
|
||||
return withContext(dispatcherIO) {
|
||||
val a = mutableListOf<Article>()
|
||||
val accountId = context.currentAccountId
|
||||
val parseRss = rssNetworkDataSource.parseRss(feed.url)
|
||||
parseRss.items.forEach {
|
||||
if (latestLink != null && latestLink == it.link) return a
|
||||
if (latestLink != null && latestLink == it.link) return@withContext a
|
||||
Log.i("RLog", "request rss ${feed.name}: ${it.title}")
|
||||
a.add(
|
||||
Article(
|
||||
|
@ -104,63 +108,57 @@ class RssHelper @Inject constructor(
|
|||
)
|
||||
)
|
||||
}
|
||||
return a
|
||||
} catch (e: Exception) {
|
||||
Log.e("RLog", "error ${feed.name}: ${e.message}")
|
||||
return a
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
suspend fun queryRssIcon(
|
||||
feedDao: FeedDao,
|
||||
feed: Feed,
|
||||
articleLink: String?,
|
||||
articleLink: String,
|
||||
) {
|
||||
try {
|
||||
if (articleLink == null) return
|
||||
withContext(dispatcherIO) {
|
||||
val execute = OkHttpClient()
|
||||
.newCall(Request.Builder().url(articleLink).build())
|
||||
.execute()
|
||||
val content = execute.body?.string()
|
||||
val content = execute.body!!.string()
|
||||
val regex =
|
||||
Regex("""<link(.+?)rel="shortcut icon"(.+?)href="(.+?)"""")
|
||||
if (content != null) {
|
||||
var iconLink = regex
|
||||
.find(content)
|
||||
?.groups?.get(3)
|
||||
?.value
|
||||
Log.i("rlog", "queryRssIcon: $iconLink")
|
||||
if (iconLink != null) {
|
||||
if (iconLink.startsWith("//")) {
|
||||
iconLink = "http:$iconLink"
|
||||
}
|
||||
if (iconLink.startsWith("/")) {
|
||||
val domainRegex =
|
||||
Regex("""http(s)?://(([\w-]+\.)+\w+(:\d{1,5})?)""")
|
||||
iconLink =
|
||||
"http://${domainRegex.find(articleLink)?.groups?.get(2)?.value}$iconLink"
|
||||
}
|
||||
saveRssIcon(feedDao, feed, iconLink)
|
||||
} else {
|
||||
// saveRssIcon(feedDao, feed, "")
|
||||
var iconLink = regex
|
||||
.find(content)
|
||||
?.groups?.get(3)
|
||||
?.value
|
||||
Log.i("rlog", "queryRssIcon: $iconLink")
|
||||
if (iconLink != null) {
|
||||
if (iconLink.startsWith("//")) {
|
||||
iconLink = "http:$iconLink"
|
||||
}
|
||||
if (iconLink.startsWith("/")) {
|
||||
val domainRegex =
|
||||
Regex("""http(s)?://(([\w-]+\.)+\w+(:\d{1,5})?)""")
|
||||
iconLink =
|
||||
"http://${domainRegex.find(articleLink)?.groups?.get(2)?.value}$iconLink"
|
||||
}
|
||||
saveRssIcon(feedDao, feed, iconLink)
|
||||
} else {
|
||||
// saveRssIcon(feedDao, feed, "")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("RLog", "queryRssIcon: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun saveRssIcon(feedDao: FeedDao, feed: Feed, iconLink: String) {
|
||||
val execute = OkHttpClient()
|
||||
.newCall(Request.Builder().url(iconLink).build())
|
||||
.execute()
|
||||
feedDao.update(
|
||||
feed.apply {
|
||||
icon = execute.body?.bytes()
|
||||
}
|
||||
)
|
||||
@Throws(Exception::class)
|
||||
suspend fun saveRssIcon(feedDao: FeedDao, feed: Feed, iconLink: String) {
|
||||
withContext(dispatcherIO) {
|
||||
val response = OkHttpClient()
|
||||
.newCall(Request.Builder().url(iconLink).build())
|
||||
.execute()
|
||||
feedDao.update(
|
||||
feed.apply {
|
||||
icon = response.body!!.bytes()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseDate(
|
||||
|
|
|
@ -2,10 +2,8 @@ package me.ash.reader.data.repository
|
|||
|
||||
import android.content.Context
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import me.ash.reader.DataStoreKeys
|
||||
import me.ash.reader.currentAccountType
|
||||
import me.ash.reader.data.account.Account
|
||||
import me.ash.reader.dataStore
|
||||
import me.ash.reader.get
|
||||
import javax.inject.Inject
|
||||
|
||||
class RssRepository @Inject constructor(
|
||||
|
@ -15,13 +13,11 @@ class RssRepository @Inject constructor(
|
|||
private val feverRssRepository: FeverRssRepository,
|
||||
// private val googleReaderRssRepository: GoogleReaderRssRepository,
|
||||
) {
|
||||
fun get() = when (getAccountType()) {
|
||||
fun get() = when (context.currentAccountType) {
|
||||
Account.Type.LOCAL -> localRssRepository
|
||||
// Account.Type.LOCAL -> feverRssRepository
|
||||
Account.Type.FEVER -> feverRssRepository
|
||||
// Account.Type.GOOGLE_READER -> googleReaderRssRepository
|
||||
else -> throw IllegalStateException("Unknown account type: ${getAccountType()}")
|
||||
else -> throw IllegalStateException("Unknown account type: ${context.currentAccountType}")
|
||||
}
|
||||
|
||||
private fun getAccountType(): Int = context.dataStore.get(DataStoreKeys.CurrentAccountType)!!
|
||||
}
|
||||
|
|
|
@ -3,11 +3,13 @@ package me.ash.reader.data.source
|
|||
import android.content.Context
|
||||
import be.ceau.opml.OpmlParser
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import me.ash.reader.*
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.ash.reader.currentAccountId
|
||||
import me.ash.reader.data.feed.Feed
|
||||
import me.ash.reader.data.group.Group
|
||||
import me.ash.reader.data.group.GroupWithFeed
|
||||
import me.ash.reader.data.repository.StringsRepository
|
||||
import me.ash.reader.data.module.DispatcherIO
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
@ -15,79 +17,77 @@ import javax.inject.Inject
|
|||
class OpmlLocalDataSource @Inject constructor(
|
||||
@ApplicationContext
|
||||
private val context: Context,
|
||||
private val stringsRepository: StringsRepository,
|
||||
@DispatcherIO
|
||||
private val dispatcherIO: CoroutineDispatcher,
|
||||
) {
|
||||
fun getDefaultGroupId(): String {
|
||||
val readYouString = stringsRepository.getString(R.string.read_you)
|
||||
val defaultString = stringsRepository.getString(R.string.defaults)
|
||||
return context.dataStore
|
||||
.get(DataStoreKeys.CurrentAccountId)!!
|
||||
.spacerDollar(readYouString + defaultString)
|
||||
}
|
||||
@Throws(Exception::class)
|
||||
suspend fun parseFileInputStream(
|
||||
inputStream: InputStream,
|
||||
defaultGroup: Group
|
||||
): List<GroupWithFeed> {
|
||||
return withContext(dispatcherIO) {
|
||||
val accountId = context.currentAccountId
|
||||
val opml = OpmlParser().parse(inputStream)
|
||||
val groupWithFeedList = mutableListOf<GroupWithFeed>().also {
|
||||
it.addGroup(defaultGroup)
|
||||
}
|
||||
|
||||
// @Throws(XmlPullParserException::class, IOException::class)
|
||||
fun parseFileInputStream(inputStream: InputStream, defaultGroup: Group): List<GroupWithFeed> {
|
||||
val accountId = context.currentAccountId
|
||||
val opml = OpmlParser().parse(inputStream)
|
||||
val groupWithFeedList = mutableListOf<GroupWithFeed>().also {
|
||||
it.addGroup(defaultGroup)
|
||||
}
|
||||
|
||||
opml.body.outlines.forEach {
|
||||
// Only feeds
|
||||
if (it.subElements.isEmpty()) {
|
||||
// It's a empty group
|
||||
if (it.attributes["xmlUrl"] == null) {
|
||||
opml.body.outlines.forEach {
|
||||
// Only feeds
|
||||
if (it.subElements.isEmpty()) {
|
||||
// It's a empty group
|
||||
if (it.attributes["xmlUrl"] == null) {
|
||||
if (!it.attributes["isDefault"].toBoolean()) {
|
||||
groupWithFeedList.addGroup(
|
||||
Group(
|
||||
id = UUID.randomUUID().toString(),
|
||||
name = it.attributes["title"] ?: it.text!!,
|
||||
accountId = accountId,
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
groupWithFeedList.addFeedToDefault(
|
||||
Feed(
|
||||
id = UUID.randomUUID().toString(),
|
||||
name = it.attributes["title"] ?: it.text!!,
|
||||
url = it.attributes["xmlUrl"]!!,
|
||||
groupId = defaultGroup.id,
|
||||
accountId = accountId,
|
||||
isNotification = it.attributes["isNotification"].toBoolean(),
|
||||
isFullContent = it.attributes["isFullContent"].toBoolean(),
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
var groupId = defaultGroup.id
|
||||
if (!it.attributes["isDefault"].toBoolean()) {
|
||||
groupId = UUID.randomUUID().toString()
|
||||
groupWithFeedList.addGroup(
|
||||
Group(
|
||||
id = UUID.randomUUID().toString(),
|
||||
id = groupId,
|
||||
name = it.attributes["title"] ?: it.text!!,
|
||||
accountId = accountId,
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
groupWithFeedList.addFeedToDefault(
|
||||
Feed(
|
||||
id = UUID.randomUUID().toString(),
|
||||
name = it.attributes["title"] ?: it.text!!,
|
||||
url = it.attributes["xmlUrl"]!!,
|
||||
groupId = defaultGroup.id,
|
||||
accountId = accountId,
|
||||
isNotification = it.attributes["isNotification"].toBoolean(),
|
||||
isFullContent = it.attributes["isFullContent"].toBoolean(),
|
||||
it.subElements.forEach { outline ->
|
||||
groupWithFeedList.addFeed(
|
||||
Feed(
|
||||
id = UUID.randomUUID().toString(),
|
||||
name = outline.attributes["title"] ?: outline.text!!,
|
||||
url = outline.attributes["xmlUrl"]!!,
|
||||
groupId = groupId,
|
||||
accountId = accountId,
|
||||
isNotification = outline.attributes["isNotification"].toBoolean(),
|
||||
isFullContent = outline.attributes["isFullContent"].toBoolean(),
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
var groupId = defaultGroup.id
|
||||
if (!it.attributes["isDefault"].toBoolean()) {
|
||||
groupId = UUID.randomUUID().toString()
|
||||
groupWithFeedList.addGroup(
|
||||
Group(
|
||||
id = groupId,
|
||||
name = it.attributes["title"] ?: it.text!!,
|
||||
accountId = accountId,
|
||||
)
|
||||
)
|
||||
}
|
||||
it.subElements.forEach { outline ->
|
||||
groupWithFeedList.addFeed(
|
||||
Feed(
|
||||
id = UUID.randomUUID().toString(),
|
||||
name = outline.attributes["title"] ?: outline.text!!,
|
||||
url = outline.attributes["xmlUrl"]!!,
|
||||
groupId = groupId,
|
||||
accountId = accountId,
|
||||
isNotification = outline.attributes["isNotification"].toBoolean(),
|
||||
isFullContent = outline.attributes["isFullContent"].toBoolean(),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
groupWithFeedList
|
||||
}
|
||||
return groupWithFeedList
|
||||
}
|
||||
|
||||
private fun MutableList<GroupWithFeed>.addGroup(group: Group) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package me.ash.reader.ui.page.home.drawer.feed
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
|
@ -8,10 +9,10 @@ import androidx.compose.material.icons.rounded.DeleteOutline
|
|||
import androidx.compose.material.icons.rounded.RssFeed
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
|
@ -31,7 +32,6 @@ fun FeedOptionDrawer(
|
|||
viewModel: FeedOptionViewModel = hiltViewModel(),
|
||||
content: @Composable () -> Unit = {},
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val viewState = viewModel.viewState.collectAsStateValue()
|
||||
val feed = viewState.feed
|
||||
|
||||
|
|
|
@ -49,14 +49,22 @@ class FeedsViewModel @Inject constructor(
|
|||
|
||||
private fun importFromInputStream(inputStream: InputStream) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
opmlRepository.saveToDatabase(inputStream)
|
||||
rssRepository.get().doSync()
|
||||
try {
|
||||
opmlRepository.saveToDatabase(inputStream)
|
||||
rssRepository.get().doSync()
|
||||
} catch (e: Exception) {
|
||||
Log.e("FeedsViewModel", "importFromInputStream: ", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun exportAsOpml(callback: (String) -> Unit = {}) {
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
opmlRepository.saveToString()?.let { callback(it) }
|
||||
try {
|
||||
callback(opmlRepository.saveToString())
|
||||
} catch (e: Exception) {
|
||||
Log.e("FeedsViewModel", "exportAsOpml: ", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,7 +82,6 @@ class FeedsViewModel @Inject constructor(
|
|||
rssRepository.get().pullFeeds(),
|
||||
rssRepository.get().pullImportant(isStarred, isUnread),
|
||||
) { groupWithFeedList, importantList ->
|
||||
Log.i("RLog", "thread:combine ${Thread.currentThread().name}")
|
||||
val groupImportantMap = mutableMapOf<String, Int>()
|
||||
val feedImportantMap = mutableMapOf<String, Int>()
|
||||
importantList.groupBy { it.groupId }.forEach { (i, list) ->
|
||||
|
@ -109,8 +116,6 @@ class FeedsViewModel @Inject constructor(
|
|||
}.onStart {
|
||||
|
||||
}.onEach { groupWithFeedList ->
|
||||
Log.i("RLog", "thread:onEach ${Thread.currentThread().name}")
|
||||
|
||||
_viewState.update {
|
||||
it.copy(
|
||||
filter = when {
|
||||
|
|
|
@ -148,7 +148,7 @@ class SubscribeViewModel @Inject constructor(
|
|||
lockLinkInput = true,
|
||||
)
|
||||
}
|
||||
if (rssRepository.get().isExist(_viewState.value.linkContent)) {
|
||||
if (rssRepository.get().isFeedExist(_viewState.value.linkContent)) {
|
||||
_viewState.update {
|
||||
it.copy(
|
||||
title = stringsRepository.getString(R.string.subscribe),
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package me.ash.reader.ui.page.home.flow
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
|
@ -55,7 +54,6 @@ class FlowViewModel @Inject constructor(
|
|||
_viewState.update {
|
||||
it.copy(
|
||||
pagingData = Pager(PagingConfig(pageSize = 10)) {
|
||||
Log.i("RLog", "thread:Pager ${Thread.currentThread().name}")
|
||||
rssRepository.get().pullArticles(
|
||||
groupId = filterState.group?.id,
|
||||
feedId = filterState.feed?.id,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package me.ash.reader.ui.page.home.read
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
|
@ -55,12 +56,23 @@ class ReadViewModel @Inject constructor(
|
|||
|
||||
private fun renderFullContent() {
|
||||
changeLoading(true)
|
||||
rssHelper.parseFullContent(
|
||||
_viewState.value.articleWithFeed?.article?.link ?: "",
|
||||
_viewState.value.articleWithFeed?.article?.title ?: ""
|
||||
) { content ->
|
||||
_viewState.update {
|
||||
it.copy(content = content)
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
_viewState.update {
|
||||
it.copy(
|
||||
content = rssHelper.parseFullContent(
|
||||
_viewState.value.articleWithFeed?.article?.link ?: "",
|
||||
_viewState.value.articleWithFeed?.article?.title ?: ""
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.i("RLog", "renderFullContent: ${e.message}")
|
||||
_viewState.update {
|
||||
it.copy(
|
||||
content = e.message
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user