Compare commits

...
18 Commits
Author SHA1 Message Date
Amine Louveau 8b0bbe71c9 Merge pull request 'Allow offline filtering' (#75) from davidoskky/ReaderForSelfoss-multiplatform:offline_filters into master
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/75
2022-10-14 07:18:42 +00:00
davidoskky 8bfe14c019 Actually filter database items 2022-10-14 00:10:35 +02:00
davidoskky 208babbce3 Correct tests 2022-10-14 00:03:20 +02:00
davidoskky 02098a7aa9 Rearrange filtering steps 2022-10-11 00:52:12 +02:00
davidoskky d0a982f385 Add tests for offline filtering 2022-10-08 17:15:41 +02:00
davidoskky 1d1c121aab Filter items from database according to tag and source 2022-10-08 17:15:22 +02:00
davidoskky fe12819163 Correct database source title 2022-10-08 17:14:12 +02:00
Amine Louveau 023a30c008 Merge pull request 'Simplify sources and tags handling' (#70) from davidoskky/ReaderForSelfoss-multiplatform:drawer_data into master
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/70
2022-10-04 18:52:39 +00:00
Amine Louveau a2862a2587 Merge pull request 'Correct mechanism of mark and unmark snackbars' (#74) from davidoskky/ReaderForSelfoss-multiplatform:snackbar-mark into master
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/74
2022-10-04 18:43:44 +00:00
Amine Louveau 054e936657 Merge pull request 'Correct boolean serialization' (#73) from davidoskky/ReaderForSelfoss-multiplatform:swiping into master
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/73
2022-10-04 18:40:14 +00:00
davidoskky 1d2e5069b8 Avoid double snackbar generation 2022-10-04 16:47:13 +02:00
davidoskky a147646743 Correct mechanism of mark and unmark snackbars 2022-10-04 16:43:21 +02:00
davidoskky 32e7fc0038 Correct boolean serialization 2022-10-04 15:01:22 +02:00
davidoskky c15bf44032 Adjust repository tests 2022-10-02 01:01:39 +02:00
davidoskky 0bcd55bd4e Add translated strings 2022-10-01 22:51:09 +02:00
davidoskky ebef0b3511 Start monitoring connectivity status when the repository is initiated. 2022-10-01 22:43:48 +02:00
davidoskky 713ceb05bf Remove unnecessary data class 2022-09-30 15:07:17 +02:00
davidoskky dc8381b661 Add missing string 2022-09-30 15:00:25 +02:00
24 changed files with 119 additions and 38 deletions
@@ -95,8 +95,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
private val repository : Repository by instance()
private val appSettingsService : AppSettingsService by instance()
data class DrawerData(val tags: List<SelfossModel.Tag>, val sources: List<SelfossModel.Source>)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHomeBinding.inflate(layoutInflater)
@@ -349,14 +347,15 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
)
CoroutineScope(Dispatchers.IO).launch {
val drawerData = DrawerData(repository.getTags(), repository.getSources())
val tags = repository.getTags()
val sources = repository.getSources()
runOnUiThread {
handleDrawerData(drawerData)
handleDrawerData(tags, sources)
}
}
}
private fun handleDrawerData(drawerData: DrawerData) {
private fun handleDrawerData(tags: List<SelfossModel.Tag>, sources: List<SelfossModel.Source>) {
binding.mainDrawer.itemAdapter.clear()
// Filters title with clear action
@@ -370,24 +369,24 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
}
// Hidden tags
if (drawerData.tags.isNotEmpty() && appSettingsService.getHiddenTags().isNotEmpty()) {
if (tags.isNotEmpty() && appSettingsService.getHiddenTags().isNotEmpty()) {
secondaryItem(
withDivider = true,
R.string.drawer_item_hidden_tags,
DRAWER_ID_HIDDEN_TAGS
)
handleHiddenTags(drawerData.tags)
handleHiddenTags(tags)
}
// Tags
secondaryItem(withDivider = true, R.string.drawer_item_tags, DRAWER_ID_TAGS)
if (drawerData.tags.isEmpty()) {
if (tags.isEmpty()) {
binding.mainDrawer.itemAdapter.add(
SecondaryDrawerItem()
.apply { nameRes = R.string.drawer_error_loading_tags; isSelectable = false }
)
} else {
handleTags(drawerData.tags)
handleTags(tags)
}
// Sources
@@ -395,15 +394,15 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
startActivity(Intent(v!!.context, SourcesActivity::class.java))
false
}
if (drawerData.sources.isEmpty()) {
if (sources.isEmpty()) {
binding.mainDrawer.itemAdapter.add(
SecondaryDrawerItem().apply {
nameRes = R.string.drawer_error_loading_tags
nameRes = R.string.drawer_error_loading_sources
isSelectable = false
}
)
} else {
handleSources(drawerData.sources)
handleSources(sources)
}
// About action
@@ -75,8 +75,6 @@ class MyApp : MultiDexApplication(), DIAware {
).show()
}
}
connectivityStatus.start()
}
private fun handleNotificationChannels() {
@@ -28,7 +28,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
updateItems(this.items)
}
private fun unmarkSnackbar(position: Int) {
private fun unmarkSnackbar(item: SelfossModel.Item, position: Int) {
val s = Snackbar
.make(
app.findViewById(R.id.coordLayout),
@@ -37,7 +37,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
)
.setAction(R.string.undo_string) {
CoroutineScope(Dispatchers.IO).launch {
unreadItemAtIndex(position, false)
unreadItemAtIndex(item, position, false)
}
}
@@ -47,7 +47,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
s.show()
}
private fun markSnackbar(position: Int) {
private fun markSnackbar(item: SelfossModel.Item, position: Int) {
val s = Snackbar
.make(
app.findViewById(R.id.coordLayout),
@@ -55,7 +55,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
Snackbar.LENGTH_LONG
)
.setAction(R.string.undo_string) {
readItemAtIndex(position)
readItemAtIndex(item, position, false)
}
val view = s.view
@@ -66,37 +66,36 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
fun handleItemAtIndex(position: Int) {
if (items[position].unread) {
readItemAtIndex(position)
readItemAtIndex(items[position], position)
} else {
unreadItemAtIndex(position)
unreadItemAtIndex(items[position], position)
}
}
private fun readItemAtIndex(position: Int, showSnackbar: Boolean = true) {
val i = items[position]
private fun readItemAtIndex(item: SelfossModel.Item, position: Int, showSnackbar: Boolean = true) {
CoroutineScope(Dispatchers.IO).launch {
repository.markAsRead(i)
repository.markAsRead(item)
}
if (repository.displayedItems == ItemType.UNREAD) {
items.remove(i)
items.remove(item)
notifyItemRemoved(position)
updateItems(items)
} else {
notifyItemChanged(position)
}
if (showSnackbar) {
unmarkSnackbar(position)
unmarkSnackbar(item, position)
}
}
private fun unreadItemAtIndex(position: Int, showSnackbar: Boolean = true) {
private fun unreadItemAtIndex(item: SelfossModel.Item, position: Int, showSnackbar: Boolean = true) {
CoroutineScope(Dispatchers.IO).launch {
repository.unmarkAsRead(items[position])
repository.unmarkAsRead(item)
}
notifyItemChanged(position)
if (showSnackbar) {
markSnackbar(position)
markSnackbar(item, position)
}
}
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Thème sombre</string>
<string name="mode_system">Utiliser les paramètres système</string>
<string name="mode_light">Thème clair</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">深色模式</string>
<string name="mode_system">遵循系统设置</string>
<string name="mode_light">浅色模式</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
+2 -1
View File
@@ -63,6 +63,7 @@
<string name="card_height_off">Card height will be fixed</string>
<string name="source_code">Source code</string>
<string name="drawer_error_loading_tags">Error loading tags…</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
<string name="drawer_item_filters">Filters</string>
<string name="drawer_action_clear">clear</string>
<string name="drawer_item_tags">Tags</string>
@@ -109,7 +110,7 @@
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
<string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string>
<string name="loading_notification_title">Loading ...</string>
<string name="loading_notification_title">Loading </string>
<string name="loading_notification_text">Selfoss is syncing your articles</string>
<string name="notification_channel_sync">Sync notification</string>
<string name="new_items_channel_sync">New items notification</string>
@@ -138,7 +138,11 @@ class SelfossModel {
object BooleanSerializer : KSerializer<Boolean> {
override fun deserialize(decoder: Decoder): Boolean {
val json = ((decoder as JsonDecoder).decodeJsonElement()).jsonPrimitive
return json.booleanOrNull ?: json.int == 1
return if (json.booleanOrNull != null) {
json.boolean
} else {
json.int == 1
}
}
override val descriptor: SerialDescriptor
@@ -42,6 +42,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
init {
// TODO: Dispatchers.IO not available in KMM, an alternative solution should be found
connectivityStatus.start()
runBlocking {
updateApiVersion()
dateUtils = DateUtils(appSettingsService)
@@ -65,12 +66,19 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
} else {
if (appSettingsService.isItemCachingEnabled()) {
fromDB = true
var dbItems = getDBItems().filter {
displayedItems == ItemType.ALL ||
(it.unread && displayedItems == ItemType.UNREAD) ||
(it.starred && displayedItems == ItemType.STARRED)
}
if (tagFilter != null) {
dbItems = dbItems.filter { it.tags.split(',').contains(tagFilter!!.tag) }
}
if (sourceFilter != null) {
dbItems = dbItems.filter { it.sourcetitle == sourceFilter!!.title }
}
fetchedItems = SelfossModel.StatusAndData.succes(
getDBItems().filter {
displayedItems == ItemType.ALL ||
(it.unread && displayedItems == ItemType.UNREAD) ||
(it.starred && displayedItems == ItemType.STARRED)
}.map { it.toView() }
dbItems.map { it.toView() }
)
}
}
@@ -65,6 +65,6 @@ fun SelfossModel.Item.toEntity(): ITEM =
this.thumbnail,
this.icon,
this.link,
this.title.getHtmlDecoded(),
this.sourcetitle.getHtmlDecoded(),
this.tags.joinToString(",")
)
@@ -42,6 +42,8 @@ class RepositoryTest() {
every { db.tagsQueries.deleteAllTags() } returns Unit
every { db.tagsQueries.transaction(any(), any()) } returns Unit
every { db.tagsQueries.insertTag(any()) } returns Unit
every { connectivityStatus.start() } returns Unit
}
@Test
@@ -171,6 +173,60 @@ class RepositoryTest() {
verify(atLeast = 1) { db.itemsQueries.items().executeAsList()}
}
@Test
fun `Get newer items without connectivity and tag filter`() {
val itemParameter1 = FakeItemParameters()
val itemParameter2 = FakeItemParameters()
val itemParameter3 = FakeItemParameters()
itemParameter2.tags = "Test, Stuff"
itemParameter2.id = "2"
itemParameter3.tags = "Other, Tag"
itemParameter3.id = "3"
coEvery { db.itemsQueries.items().executeAsList() } returns generateTestDBItems(itemParameter1) +
generateTestDBItems(itemParameter2) +
generateTestDBItems(itemParameter3)
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
every { appSettingsService.isItemCachingEnabled() } returns true
val repository = Repository(api, appSettingsService, connectivityStatus, db)
repository.tagFilter = SelfossModel.Tag("Test", "red", 3)
runBlocking {
repository.getNewerItems()
}
assertSame(repository.items.size, 1)
coVerify(exactly = 0) { api.getItems("unread", 0, null, null, null, null, any()) }
verify(atLeast = 1) { db.itemsQueries.items().executeAsList()}
}
@Test
fun `Get newer items without connectivity and source filter`() {
val itemParameter1 = FakeItemParameters()
val itemParameter2 = FakeItemParameters()
val itemParameter3 = FakeItemParameters()
itemParameter2.sourcetitle = "Test"
itemParameter2.id = "2"
itemParameter3.sourcetitle = "Other"
itemParameter3.id = "3"
coEvery { db.itemsQueries.items().executeAsList() } returns generateTestDBItems(itemParameter1) +
generateTestDBItems(itemParameter2) +
generateTestDBItems(itemParameter3)
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
every { appSettingsService.isItemCachingEnabled() } returns true
val repository = Repository(api, appSettingsService, connectivityStatus, db)
repository.sourceFilter = SelfossModel.Source(1, "Test", listOf("tags"),"spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")
runBlocking {
repository.getNewerItems()
}
assertSame(repository.items.size, 1)
coVerify(exactly = 0) { api.getItems("unread", 0, null, null, null, null, any()) }
verify(atLeast = 1) { db.itemsQueries.items().executeAsList()}
}
@Test
fun `Get older items`() {
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
@@ -996,6 +1052,6 @@ class FakeItemParameters() {
val thumbnail = null
val icon = "ba79e238383ce83c23a169929c8906ef.png"
val link = "https://ilblogdellasci.wordpress.com/2022/09/09/etica-della-ricerca-sotto-i-riflettori/"
val sourcetitle = "La Chimica e la Società"
val tags = "Chimica, Testing"
var sourcetitle = "La Chimica e la Società"
var tags = "Chimica, Testing"
}