Compare commits

..
Author SHA1 Message Date
davidoskky 566930ba84 Fix sourceDetails unit tests 2025-04-13 00:01:46 +02:00
davidoskky b1259fdc92 Support source filter editing
The API for filter is undocumented on the Selfoss API reference, but does exist. We can now allow users setting and modifying filters inside the application.
2025-04-12 23:58:12 +02:00
7 changed files with 65 additions and 17 deletions
@@ -65,6 +65,7 @@ class UpsertSourceActivity :
private fun initFields(items: Map<String, SelfossModel.Spout>) { private fun initFields(items: Map<String, SelfossModel.Spout>) {
binding.nameInput.setText(existingSource!!.title) binding.nameInput.setText(existingSource!!.title)
binding.tags.setText(existingSource!!.tags?.joinToString(", ")) binding.tags.setText(existingSource!!.tags?.joinToString(", "))
binding.filter.setText(existingSource!!.filter)
binding.sourceUri.setText(existingSource!!.params?.url) binding.sourceUri.setText(existingSource!!.params?.url)
binding.spoutsSpinner.setSelection(items.keys.indexOf(existingSource!!.spout)) binding.spoutsSpinner.setSelection(items.keys.indexOf(existingSource!!.spout))
binding.progress.visibility = View.GONE binding.progress.visibility = View.GONE
@@ -169,6 +170,7 @@ class UpsertSourceActivity :
url, url,
mSpoutsValue!!, mSpoutsValue!!,
binding.tags.text.toString(), binding.tags.text.toString(),
binding.filter.text.toString(),
) )
} else { } else {
repository.createSource( repository.createSource(
@@ -176,6 +178,7 @@ class UpsertSourceActivity :
url, url,
mSpoutsValue!!, mSpoutsValue!!,
binding.tags.text.toString(), binding.tags.text.toString(),
binding.filter.text.toString(),
) )
} }
if (successfullyAddedSource) { if (successfullyAddedSource) {
@@ -64,15 +64,28 @@
android:id="@+id/tags" android:id="@+id/tags"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minHeight="48dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:autofillHints="false" android:autofillHints="false"
android:hint="@string/add_source_hint_tags" android:hint="@string/add_source_hint_tags"
android:inputType="text" android:inputType="text"
android:minHeight="48dp"
android:textColorHint="?android:textColorPrimary" android:textColorHint="?android:textColorPrimary"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sourceUri" /> app:layout_constraintTop_toBottomOf="@+id/sourceUri" />
<EditText
android:id="@+id/filter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:autofillHints="false"
android:hint="@string/add_source_hint_filter"
android:inputType="text"
android:minHeight="48dp"
android:textColorHint="?android:textColorPrimary"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tags" />
<Spinner <Spinner
android:id="@+id/spoutsSpinner" android:id="@+id/spoutsSpinner"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -80,7 +93,7 @@
android:minHeight="48dp" android:minHeight="48dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tags" /> app:layout_constraintTop_toBottomOf="@+id/filter" />
<Button <Button
android:id="@+id/saveBtn" android:id="@+id/saveBtn"
@@ -17,6 +17,7 @@
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string> <string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
<string name="add_source_hint_url">"Link"</string> <string name="add_source_hint_url">"Link"</string>
<string name="add_source_hint_name">"Name"</string> <string name="add_source_hint_name">"Name"</string>
<string name="add_source_hint_filter">/Filter/</string>
<string name="add_source">"Add a source"</string> <string name="add_source">"Add a source"</string>
<string name="add_source_save">"Save"</string> <string name="add_source_save">"Save"</string>
<string name="wrong_infos">"Check your details again."</string> <string name="wrong_infos">"Check your details again."</string>
@@ -44,6 +44,8 @@ private const val FEED_URL = "https://test.com/feed"
private const val TAGS = "Test, New" private const val TAGS = "Test, New"
private const val FILTER = "/filter/"
private const val NUMBER_ARTICLES = 100 private const val NUMBER_ARTICLES = 100
private const val NUMBER_UNREAD = 50 private const val NUMBER_UNREAD = 50
private const val NUMBER_STARRED = 20 private const val NUMBER_STARRED = 20
@@ -836,7 +838,7 @@ class RepositoryTest {
@Test @Test
fun create_source() { fun create_source() {
coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns coEvery { api.createSourceForVersion(any(), any(), any(), any(), any()) } returns
SuccessResponse(true) SuccessResponse(true)
initializeRepository() initializeRepository()
@@ -848,6 +850,7 @@ class RepositoryTest {
FEED_URL, FEED_URL,
SPOUT, SPOUT,
TAGS, TAGS,
FILTER,
) )
} }
@@ -857,6 +860,7 @@ class RepositoryTest {
any(), any(),
any(), any(),
any(), any(),
any(),
) )
} }
assertSame(true, response) assertSame(true, response)
@@ -864,7 +868,7 @@ class RepositoryTest {
@Test @Test
fun create_source_but_response_fails() { fun create_source_but_response_fails() {
coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns coEvery { api.createSourceForVersion(any(), any(), any(), any(), any()) } returns
SuccessResponse(false) SuccessResponse(false)
initializeRepository() initializeRepository()
@@ -876,6 +880,7 @@ class RepositoryTest {
FEED_URL, FEED_URL,
SPOUT, SPOUT,
TAGS, TAGS,
FILTER,
) )
} }
@@ -885,6 +890,7 @@ class RepositoryTest {
any(), any(),
any(), any(),
any(), any(),
any(),
) )
} }
assertSame(false, response) assertSame(false, response)
@@ -892,7 +898,7 @@ class RepositoryTest {
@Test @Test
fun create_source_without_connection() { fun create_source_without_connection() {
coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns coEvery { api.createSourceForVersion(any(), any(), any(), any(), any()) } returns
SuccessResponse(true) SuccessResponse(true)
initializeRepository(false) initializeRepository(false)
@@ -904,6 +910,7 @@ class RepositoryTest {
FEED_URL, FEED_URL,
SPOUT, SPOUT,
TAGS, TAGS,
FILTER,
) )
} }
@@ -913,6 +920,7 @@ class RepositoryTest {
any(), any(),
any(), any(),
any(), any(),
any(),
) )
} }
assertSame(false, response) assertSame(false, response)
@@ -102,6 +102,7 @@ class SelfossModel {
override var error: String? = null, override var error: String? = null,
override var icon: String? = null, override var icon: String? = null,
var params: SourceParams? = null, var params: SourceParams? = null,
var filter: String? = null,
) : Source ) : Source
@Serializable @Serializable
@@ -368,6 +368,7 @@ class Repository(
url: String, url: String,
spout: String, spout: String,
tags: String, tags: String,
filter: String,
): Boolean { ): Boolean {
var response = false var response = false
if (connectivityService.isNetworkAvailable()) { if (connectivityService.isNetworkAvailable()) {
@@ -377,6 +378,7 @@ class Repository(
url, url,
spout, spout,
tags, tags,
filter,
).isSuccess == true ).isSuccess == true
} }
@@ -389,10 +391,11 @@ class Repository(
url: String, url: String,
spout: String, spout: String,
tags: String, tags: String,
filter: String,
): Boolean { ): Boolean {
var response = false var response = false
if (connectivityService.isNetworkAvailable()) { if (connectivityService.isNetworkAvailable()) {
response = api.updateSourceForVersion(id, title, url, spout, tags).isSuccess == true response = api.updateSourceForVersion(id, title, url, spout, tags, filter).isSuccess == true
} }
return response return response
@@ -19,7 +19,6 @@ import io.ktor.client.plugins.cookies.HttpCookies
import io.ktor.client.plugins.logging.LogLevel import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger import io.ktor.client.plugins.logging.Logger
import io.ktor.client.plugins.logging.Logging import io.ktor.client.plugins.logging.Logging
import io.ktor.client.plugins.sse.SSE
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.client.request.headers import io.ktor.client.request.headers
import io.ktor.client.request.parameter import io.ktor.client.request.parameter
@@ -90,7 +89,6 @@ class SelfossApi(
} }
} }
} }
install(SSE)
expectSuccess = false expectSuccess = false
} }
@@ -365,11 +363,26 @@ class SelfossApi(
suspend fun update(): StatusAndData<String> = suspend fun update(): StatusAndData<String> =
bodyOrFailure( bodyOrFailure(
client.tryToGet(url("/update")) { client.tryToGet(url("/update")) {
headers { if (!shouldHavePostLogin()) {
append( parameter("username", appSettingsService.getUserName())
HttpHeaders.Accept, parameter("password", appSettingsService.getPassword())
"text/event-stream", }
) if (appSettingsService
.getBasicUserName()
.isNotEmpty() &&
appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
constructBasicAuthValue(
BasicAuthCredentials(
username = appSettingsService.getBasicUserName(),
password = appSettingsService.getBasicPassword(),
),
),
)
}
} }
}, },
) )
@@ -625,12 +638,13 @@ class SelfossApi(
url: String, url: String,
spout: String, spout: String,
tags: String, tags: String,
filter: String,
): SuccessResponse = ): SuccessResponse =
maybeResponse( maybeResponse(
if (appSettingsService.getApiVersion() > 1) { if (appSettingsService.getApiVersion() > 1) {
createSource("tags[]", title, url, spout, tags) createSource("tags[]", title, url, spout, tags, filter)
} else { } else {
createSource("tags", title, url, spout, tags) createSource("tags", title, url, spout, tags, filter)
}, },
) )
@@ -640,6 +654,7 @@ class SelfossApi(
url: String, url: String,
spout: String, spout: String,
tags: String, tags: String,
filter: String,
): HttpResponse? = ): HttpResponse? =
client.tryToSubmitForm( client.tryToSubmitForm(
url = url("/source"), url = url("/source"),
@@ -652,6 +667,7 @@ class SelfossApi(
append("title", title) append("title", title)
append("url", url) append("url", url)
append("spout", spout) append("spout", spout)
append("filter", filter)
append(tagsParamName, tags) append(tagsParamName, tags)
}, },
block = { block = {
@@ -681,12 +697,13 @@ class SelfossApi(
url: String, url: String,
spout: String, spout: String,
tags: String, tags: String,
filter: String,
): SuccessResponse = ): SuccessResponse =
maybeResponse( maybeResponse(
if (appSettingsService.getApiVersion() > 1) { if (appSettingsService.getApiVersion() > 1) {
updateSource(id, "tags[]", title, url, spout, tags) updateSource(id, "tags[]", title, url, spout, tags, filter)
} else { } else {
updateSource(id, "tags", title, url, spout, tags) updateSource(id, "tags", title, url, spout, tags, filter)
}, },
) )
@@ -697,6 +714,7 @@ class SelfossApi(
url: String, url: String,
spout: String, spout: String,
tags: String, tags: String,
filter: String,
): HttpResponse? = ): HttpResponse? =
client.tryToSubmitForm( client.tryToSubmitForm(
url = url("/source/$id"), url = url("/source/$id"),
@@ -710,6 +728,7 @@ class SelfossApi(
append("url", url) append("url", url)
append("spout", spout) append("spout", spout)
append(tagsParamName, tags) append(tagsParamName, tags)
append("filter", filter)
}, },
block = { block = {
if (appSettingsService if (appSettingsService