From c127271edeab3495f9bba550d017c5a76bba64a7 Mon Sep 17 00:00:00 2001 From: davidoskky Date: Sun, 12 Mar 2023 16:53:17 +0100 Subject: [PATCH] Implement the different Source types through an interface --- .../android/SourcesActivity.kt | 2 +- .../android/UpsertSourceActivity.kt | 2 +- .../android/adapters/SourcesListAdapter.kt | 2 +- .../android/fragments/FilterSheetFragment.kt | 2 +- .../readerforselfossv2/model/SelfossModel.kt | 144 ++++++++++++------ .../repository/RepositoryImpl.kt | 61 +++++--- .../readerforselfossv2/utils/EntityUtils.kt | 16 +- .../amine/apps/readerforselfossv2/dao/3.sqm | 1 - .../apps/readerforselfossv2/dao/Sources.sq | 1 - 9 files changed, 136 insertions(+), 95 deletions(-) delete mode 100644 shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/3.sqm diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/SourcesActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/SourcesActivity.kt index c677952..ca47080 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/SourcesActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/SourcesActivity.kt @@ -49,7 +49,7 @@ class SourcesActivity : AppCompatActivity(), DIAware { super.onResume() val mLayoutManager = LinearLayoutManager(this) - var items: ArrayList + var items: ArrayList binding.recyclerView.setHasFixedSize(true) binding.recyclerView.layoutManager = mLayoutManager diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/UpsertSourceActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/UpsertSourceActivity.kt index d166d90..c24babe 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/UpsertSourceActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/UpsertSourceActivity.kt @@ -24,7 +24,7 @@ import org.kodein.di.instance class UpsertSourceActivity : AppCompatActivity(), DIAware { - private var existingSource: SelfossModel.Source? = null + private var existingSource: SelfossModel.SourceDetail? = null private var mSpoutsValue: String? = null private lateinit var binding: ActivityUpsertSourceBinding diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/SourcesListAdapter.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/SourcesListAdapter.kt index 65ae592..2eba4c1 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/SourcesListAdapter.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/SourcesListAdapter.kt @@ -31,7 +31,7 @@ import org.kodein.di.instance class SourcesListAdapter( private val app: Activity, - private val items: ArrayList + private val items: ArrayList ) : RecyclerView.Adapter(), DIAware { private val c: Context = app.baseContext private val generator: ColorGenerator = ColorGenerator.MATERIAL diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/FilterSheetFragment.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/FilterSheetFragment.kt index 832906b..53b5932 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/FilterSheetFragment.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/FilterSheetFragment.kt @@ -84,7 +84,7 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware { ) { val sourceGroup = binding.sourcesGroup - repository.getSourcesStats().forEach { source -> + repository.getSourcesDetailsOrStats().forEach { source -> val c = Chip(context) c.ellipsize = TextUtils.TruncateAt.END diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt index e0733f1..be7d0ab 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt @@ -1,5 +1,6 @@ package bou.amine.apps.readerforselfossv2.model +import bou.amine.apps.readerforselfossv2.dao.SOURCE import bou.amine.apps.readerforselfossv2.utils.DateUtils import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded import kotlinx.serialization.KSerializer @@ -63,70 +64,113 @@ class SelfossModel { fun isPublicModeEnabled() = publicMode ?: false } - data class Source( - val id: Int, - val title: String, - var unread: Int?, - var tags: List?, - var spout: String?, - var error: String?, - var icon: String?, - var params: SourceParams? - ) { + interface Source { + val id: Int + var title: String + var unread: Int? + var error: String? + var icon: String? - constructor(sourceDetail: SourceDetail) : this( - id = sourceDetail.id, - title = sourceDetail.title, - unread = null, - tags = sourceDetail.tags, - spout = sourceDetail.spout, - error = sourceDetail.error, - icon = sourceDetail.icon, - params = sourceDetail.params - ) - - constructor(sourceStat: SourceStats) : this( - id = sourceStat.id, - title = sourceStat.title, - unread = sourceStat.unread, - tags = null, - spout = null, - error = null, - icon = null, - params = null - ) + fun checkSameSource(source: Source): Boolean { + return this.id != source.id || this.title != source.title + } fun update(source: Source) { - if (this.id != source.id || this.title != source.title) { + if (source is SourceDetail) { + source.update(source) + } else if (source is SourceStats) { + source.update(source) + } + } + fun update(source: SourceStats) + + fun update(source: SourceDetail) + + fun toEntity(): SOURCE + + operator fun component1(): Int { return id } + operator fun component2(): String { return title } + } + + @Serializable + data class SourceStats( + override val id: Int, + override var title: String, + override var unread: Int?, + override var error: String? = null, + override var icon: String? = null + ) : Source { + override fun update(source: SourceStats) { + if (checkSameSource(source)) { throw Exception() } this.unread = source.unread + } + + override fun update(source: SourceDetail) { + if (checkSameSource(source)) { + throw Exception() + } + this.error = source.error + this.icon = source.icon + } + + override fun toEntity(): SOURCE { + return SOURCE( + this.id.toString(), + this.title.getHtmlDecoded(), + null, + null, + this.error, + this.icon, + null + ) + } + } + + @Serializable + data class SourceDetail( + override val id: Int, + override var title: String, + override var unread: Int? = null, + @Serializable(with = TagsListSerializer::class) + var tags: List?, + var spout: String?, + override var error: String?, + override var icon: String?, + var params: SourceParams? + ) : Source { + override fun update(source: SourceStats) { + if (checkSameSource(source)) { + throw Exception() + } + this.unread = source.unread + } + + override fun update(source: SourceDetail) { + if (checkSameSource(source)) { + throw Exception() + } this.tags = source.tags this.spout = source.spout this.error = source.error this.icon = source.icon this.params = source.params } + + override fun toEntity(): SOURCE { + return SOURCE( + this.id.toString(), + this.title.getHtmlDecoded(), + this.tags?.joinToString(","), + this.spout, + this.error, + this.icon.orEmpty(), + this.params?.url + ) + } } - @Serializable - data class SourceStats( - val id: Int, - val title: String, - val unread: Int - ) - - @Serializable - data class SourceDetail( - val id: Int, - val title: String, - @Serializable(with = TagsListSerializer::class) - val tags: List, - val spout: String, - val error: String, - val icon: String?, - val params: SourceParams? - ) @Serializable data class SourceParams( val url: String diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt index 078d842..f7a6e15 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt @@ -44,12 +44,11 @@ class Repository( private val _badgeStarred = MutableStateFlow(0) val badgeStarred = _badgeStarred.asStateFlow() - private var sources = ArrayList() - private var fetchedTags = false + private var fetchedSources = false private var _readerItems = ArrayList() - private var _selectedSource: SelfossModel.Source? = null + private var _selectedSource: SelfossModel.SourceDetail? = null suspend fun getNewerItems(): ArrayList { var fetchedItems: StatusAndData> = StatusAndData.error() @@ -181,47 +180,60 @@ class Repository( } } - private fun updateSources(newSources: ArrayList) { + private fun updateSources(newSources: List): List { val isDatabaseEnabled = appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled() - val newIds = newSources.map {it.id} - val filteredSources = sources.filterNot { it.id in newIds } - for (source in filteredSources) { - val newSource = newSources.find { it.id == source.id } - source.update(newSource!!) - } - sources = (filteredSources + newSources).distinctBy { it.id } as ArrayList + return if (isDatabaseEnabled) { + var sources = getDBSources().map { it.toView() } + for (source in sources) { + val newSource = newSources.find { it.id == source.id } + if (newSource != null) { + source.update(newSource) + } + } + + sources = (sources + newSources).distinctBy { it.id } - if (isDatabaseEnabled) { resetDBSourcesWithData(sources) + sources + } else { + newSources } } - suspend fun getSourcesStats(): ArrayList { + suspend fun getSourcesDetailsOrStats(): ArrayList { + var sources = ArrayList() val isDatabaseEnabled = appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled() - if (isNetworkAvailable() && sources.none { it.unread != null }) { - val apiSources = api.sourcesStats() - if (apiSources.success && apiSources.data != null) { - updateSources(apiSources.data.map { SelfossModel.Source(it) } as ArrayList) + val shouldFetch = if (appSettingsService.isUpdateSourcesEnabled()) !fetchedSources else true + if (shouldFetch && isNetworkAvailable()) { + if (appSettingsService.getPublicAccess()) { + val apiSources = api.sourcesStats() + if (apiSources.success && apiSources.data != null) { + sources = updateSources(apiSources.data) as ArrayList + } + } else { + sources = getSourcesDetails() as ArrayList } } else if (isDatabaseEnabled) { - updateSources(getDBSources().map { it.toView() } as ArrayList) + sources = getDBSources().map { it.toView() } as ArrayList } return sources } - suspend fun getSourcesDetails(): ArrayList { + suspend fun getSourcesDetails(): ArrayList { + var sources = ArrayList() val isDatabaseEnabled = appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled() - if (isNetworkAvailable() && sources.none { it.spout != null }) { + val shouldFetch = if (appSettingsService.isUpdateSourcesEnabled()) !fetchedSources else true + if (shouldFetch && isNetworkAvailable()) { val apiSources = api.sourcesDetailed() if (apiSources.success && apiSources.data != null) { - updateSources(apiSources.data.map { SelfossModel.Source(it) } as ArrayList) + sources = updateSources(apiSources.data) as ArrayList } } else if (isDatabaseEnabled) { - updateSources(getDBSources().map { it.toView() } as ArrayList) + sources = getDBSources().map { it.toView() } as ArrayList } return sources } @@ -400,7 +412,6 @@ class Repository( items = ArrayList(items.filter { it.sourcetitle != title }) setReaderItems(items) db.itemsQueries.deleteItemsWhereSource(title) - sources.removeAll { it.id == id } } return success @@ -620,7 +631,7 @@ class Repository( ReaderForSelfossDB.Schema.migrate(driverFactory.createDriver(), 0, 1) } - fun setSelectedSource(source: SelfossModel.Source) { + fun setSelectedSource(source: SelfossModel.SourceDetail) { _selectedSource = source } @@ -628,7 +639,7 @@ class Repository( _selectedSource = null } - fun getSelectedSource(): SelfossModel.Source? { + fun getSelectedSource(): SelfossModel.SourceDetail? { return _selectedSource } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/EntityUtils.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/EntityUtils.kt index 8b932ae..05e0b60 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/EntityUtils.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/EntityUtils.kt @@ -13,10 +13,10 @@ fun TAG.toView(): SelfossModel.Tag = ) fun SOURCE.toView(): SelfossModel.Source = - SelfossModel.Source( + SelfossModel.SourceDetail( this.id.toInt(), this.title, - this.unread?.toInt(), + null, this.tags?.split(","), this.spout, this.error, @@ -24,18 +24,6 @@ fun SOURCE.toView(): SelfossModel.Source = if (this.url != null) SelfossModel.SourceParams(this.url) else null ) -fun SelfossModel.Source.toEntity(): SOURCE = - SOURCE( - this.id.toString(), - this.title.getHtmlDecoded(), - this.unread?.toLong(), - this.tags?.joinToString(","), - this.spout, - this.error, - this.icon.orEmpty(), - this.params?.url - ) - fun SelfossModel.Tag.toEntity(): TAG = TAG( this.tag, diff --git a/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/3.sqm b/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/3.sqm deleted file mode 100644 index fc42709..0000000 --- a/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/3.sqm +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE SOURCE ADD COLUMN `unread` INTEGER; \ No newline at end of file diff --git a/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Sources.sq b/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Sources.sq index 249a86d..b5332d8 100644 --- a/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Sources.sq +++ b/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Sources.sq @@ -1,7 +1,6 @@ CREATE TABLE SOURCE ( `id` TEXT NOT NULL, `title` TEXT NOT NULL, - `unread` INTEGER, `tags` TEXT, `spout` TEXT, `error` TEXT,