Compare commits

..

10 Commits

Author SHA1 Message Date
dd81fd95f8 Source update screen.
Some checks failed
continuous-integration/drone/push Build is failing
2022-12-28 22:09:31 +01:00
c38251f5b3 Sources menu. 2022-12-28 21:45:00 +01:00
a01f6d2322 chore: Automatic CHANGELOG generation.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-28 21:43:56 +01:00
417a33eb25 Merge pull request 'Running migrations.' (#118) from fix-migration into master
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/118
2022-12-28 14:49:11 +00:00
2e7f7f23b3 No duplicate builds for PRs.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-28 15:34:22 +01:00
e5e182761e Running migrations.
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/pr Build was killed
2022-12-28 15:27:17 +01:00
a094d88799 Merge pull request 'Make the author field nullable' (#117) from davidoskky/ReaderForSelfoss-multiplatform:author into master
Some checks reported errors
continuous-integration/drone/push Build was killed
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/117
2022-12-28 14:25:36 +00:00
e51915d1cd Include author field when updating the database
All checks were successful
continuous-integration/drone/pr Build is passing
2022-12-28 14:25:56 +01:00
3a654f6ede Migrate the database table 2022-12-28 14:25:34 +01:00
5227751dca Make the author field nullable
All checks were successful
continuous-integration/drone/pr Build is passing
2022-12-28 11:02:43 +01:00
38 changed files with 279 additions and 113 deletions

View File

@ -35,7 +35,6 @@ steps:
trigger:
event:
- push
- pull_request
---
kind: pipeline
@ -43,12 +42,18 @@ type: docker
name: Publish
steps:
- name: createTag
- name: createTagAndChangelog
image: ubuntu:latest
commands:
- apt-get update && apt-get install -y git
- VER=$(git describe --tags --abbrev=0)
- CHANGELOG=$(git log $VER..HEAD --pretty="- %s")
- echo "**$VER**\n\n$CHANGELOG\n\n--------------------------------------------------------------------\n\n$(cat CHANGELOG.md)" > CHANGELOG.md
- git add CHANGELOG.md
- git commit -m "Changelog for $VER"
- ./build.sh --publish --from-ci
- git remote add pushing https://$GITEA_USR:$GITEA_PASS@gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform.git
- git push pushing master
- git push pushing --tags
environment:
TZ: Europe/Paris

View File

@ -52,7 +52,7 @@
android:value=".HomeActivity" />
</activity>
<activity
android:name=".AddSourceActivity"
android:name=".UpsertSourceActivity"
android:parentActivityName=".SourcesActivity"
android:exported="true">
<meta-data

View File

@ -603,6 +603,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
settingsLauncher.launch(Intent(this, SettingsActivity::class.java))
return true
}
R.id.action_sources -> {
startActivity(Intent(this, SourcesActivity::class.java))
return true
}
else -> return super.onOptionsItemSelected(item)
}
}

View File

@ -3,10 +3,7 @@ package bou.amine.apps.readerforselfossv2.android
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build
import android.widget.ImageView
import android.widget.Toast
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
@ -18,8 +15,6 @@ import bou.amine.apps.readerforselfossv2.dao.DriverFactory
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.github.ln_12.library.ConnectivityStatus
import io.github.aakira.napier.DebugAntilog
import io.github.aakira.napier.Napier
@ -83,6 +78,8 @@ class MyApp : MultiDexApplication(), DIAware {
}
}
}
repository.migrate(driverFactory)
}
override fun attachBaseContext(base: Context?) {

View File

@ -73,7 +73,7 @@ class SourcesActivity : AppCompatActivity(), DIAware {
}
binding.fab.setOnClickListener {
startActivity(Intent(this@SourcesActivity, AddSourceActivity::class.java))
startActivity(Intent(this@SourcesActivity, UpsertSourceActivity::class.java))
}
}
}

View File

@ -3,12 +3,15 @@ package bou.amine.apps.readerforselfossv2.android
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.*
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityAddSourceBinding
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityUpsertSourceBinding
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid
import bou.amine.apps.readerforselfossv2.model.NetworkUnavailableException
import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import kotlinx.coroutines.CoroutineScope
@ -19,38 +22,59 @@ import org.kodein.di.android.closestDI
import org.kodein.di.instance
class AddSourceActivity : AppCompatActivity(), DIAware {
class UpsertSourceActivity : AppCompatActivity(), DIAware {
private var existingSource: SelfossModel.Source? = null
private var mSpoutsValue: String? = null
private lateinit var binding: ActivityAddSourceBinding
private lateinit var binding: ActivityUpsertSourceBinding
override val di by closestDI()
private val repository : Repository by instance()
private val appSettingsService : AppSettingsService by instance()
private val repository: Repository by instance()
private val appSettingsService: AppSettingsService by instance()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAddSourceBinding.inflate(layoutInflater)
binding = ActivityUpsertSourceBinding.inflate(layoutInflater)
val view = binding.root
existingSource = repository.getSelectedSource()
if (existingSource != null) {
binding.formContainer.visibility = View.GONE
binding.progress.visibility = View.VISIBLE
}
val title = if (existingSource == null) R.string.add_source else R.string.update_source
supportFragmentManager.addOnBackStackChangedListener {
if (supportFragmentManager.backStackEntryCount == 0) {
setTitle(title)
}
}
setContentView(view)
setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true)
supportActionBar?.title = resources.getString(title)
maybeGetDetailsFromIntentSharing(intent, binding.sourceUri, binding.nameInput)
maybeGetDetailsFromIntentSharing(intent)
binding.saveBtn.setOnClickListener {
handleSaveSource(
binding.tags,
binding.nameInput.text.toString(),
binding.sourceUri.text.toString()
)
handleSaveSource()
}
}
private fun initFields(items: Map<String, SelfossModel.Spout>) {
binding.nameInput.setText(existingSource!!.title)
binding.tags.setText(existingSource!!.tags.joinToString(", "))
binding.sourceUri.setText(existingSource!!.params?.url)
binding.spoutsSpinner.setSelection(items.keys.indexOf(existingSource!!.spout))
binding.progress.visibility = View.GONE
binding.formContainer.visibility = View.VISIBLE
}
override fun onResume() {
super.onResume()
@ -58,17 +82,13 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
if (baseUrl.isEmpty() || baseUrl.isBaseUrlInvalid()) {
mustLoginToAddSource()
} else {
handleSpoutsSpinner(binding.spoutsSpinner, binding.progress, binding.formContainer)
handleSpoutsSpinner()
}
}
private fun handleSpoutsSpinner(
spoutsSpinner: Spinner,
mProgress: ProgressBar,
formContainer: ConstraintLayout
) {
private fun handleSpoutsSpinner() {
val spoutsKV = HashMap<String, String>()
spoutsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
binding.spoutsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(adapterView: AdapterView<*>, view: View?, i: Int, l: Long) {
if (view != null) {
val spoutName = (view as TextView).text.toString()
@ -84,11 +104,11 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
fun handleSpoutFailure(networkIssue: Boolean = false) {
Toast.makeText(
this@AddSourceActivity,
this@UpsertSourceActivity,
if (networkIssue) R.string.cant_get_spouts_no_network else R.string.cant_get_spouts,
Toast.LENGTH_SHORT
).show()
mProgress.visibility = View.GONE
binding.progress.visibility = View.GONE
}
CoroutineScope(Dispatchers.Main).launch {
@ -100,17 +120,21 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
spoutsKV[value.name] = key
}
mProgress.visibility = View.GONE
formContainer.visibility = View.VISIBLE
binding.progress.visibility = View.GONE
binding.formContainer.visibility = View.VISIBLE
val spinnerArrayAdapter =
ArrayAdapter(
this@AddSourceActivity,
this@UpsertSourceActivity,
android.R.layout.simple_spinner_item,
itemsStrings
)
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spoutsSpinner.adapter = spinnerArrayAdapter
binding.spoutsSpinner.adapter = spinnerArrayAdapter
if (existingSource != null) {
initFields(items)
}
} else {
handleSpoutFailure()
}
@ -121,13 +145,11 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
}
private fun maybeGetDetailsFromIntentSharing(
intent: Intent,
sourceUri: EditText,
nameInput: EditText
intent: Intent
) {
if (Intent.ACTION_SEND == intent.action && "text/plain" == intent.type) {
sourceUri.setText(intent.getStringExtra(Intent.EXTRA_TEXT))
nameInput.setText(intent.getStringExtra(Intent.EXTRA_TITLE))
binding.sourceUri.setText(intent.getStringExtra(Intent.EXTRA_TEXT))
binding.nameInput.setText(intent.getStringExtra(Intent.EXTRA_TITLE))
}
}
@ -138,7 +160,8 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
finish()
}
private fun handleSaveSource(tags: EditText, title: String, url: String) {
private fun handleSaveSource() {
val url = binding.sourceUri.text.toString()
val sourceDetailsUnavailable =
title.isEmpty() || url.isEmpty() || mSpoutsValue == null || mSpoutsValue!!.isEmpty()
@ -149,18 +172,27 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
}
else -> {
CoroutineScope(Dispatchers.Main).launch {
val successfullyAddedSource = repository.createSource(
title,
url,
mSpoutsValue!!,
tags.text.toString(),
"",
)
val successfullyAddedSource = if (existingSource != null) {
repository.updateSource(
existingSource!!.id,
binding.nameInput.text.toString(),
url,
mSpoutsValue!!,
binding.tags.text.toString()
)
} else {
repository.createSource(
binding.nameInput.text.toString(),
url,
mSpoutsValue!!,
binding.tags.text.toString(),
)
}
if (successfullyAddedSource) {
finish()
} else {
Toast.makeText(
this@AddSourceActivity,
this@UpsertSourceActivity,
R.string.cant_create_source,
Toast.LENGTH_SHORT
).show()
@ -169,4 +201,9 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
}
}
}
override fun onDestroy() {
super.onDestroy()
repository.unsetSelectedSource()
}
}

View File

@ -2,12 +2,14 @@ package bou.amine.apps.readerforselfossv2.android.adapters
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.Button
import android.widget.Toast
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.RecyclerView
import bou.amine.apps.readerforselfossv2.android.UpsertSourceActivity
import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.databinding.SourceListItemBinding
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
@ -94,6 +96,14 @@ class SourcesListAdapter(
}
}
}
mView.setOnClickListener {
val source = items[bindingAdapterPosition]
repository.setSelectedSource(source)
app.startActivity(Intent(app, UpsertSourceActivity::class.java))
}
}
}
}

View File

@ -46,7 +46,7 @@ data class ParecelableItem(
val link: String,
val sourcetitle: String,
val tags: String,
val author: String
val author: String?
) : Parcelable {
companion object {

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="bou.amine.apps.readerforselfossv2.android.AddSourceActivity">
tools:context="bou.amine.apps.readerforselfossv2.android.UpsertSourceActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -41,36 +41,20 @@
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintVertical_bias="0.0">
<TextView
android:text="@string/add_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView2"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:textAlignment="center"
android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginEnd="16dp"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginLeft="16dp"
android:gravity="center_horizontal" />
<EditText
android:id="@+id/nameInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:autofillHints="false"
android:ems="10"
android:id="@+id/nameInput"
android:layout_marginTop="32dp"
app:layout_constraintTop_toBottomOf="@+id/textView2"
android:hint="@string/add_source_hint_name"
android:inputType="text"
android:textColorHint="?android:textColorPrimary"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:inputType="text"
android:hint="@string/add_source_hint_name"
android:textColorHint="?android:textColorPrimary"
android:autofillHints="false" />
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:layout_width="match_parent"

View File

@ -20,6 +20,11 @@
android:orderInCategory="2"
app:showAsAction="ifRoom"/>
<item android:id="@+id/action_sources"
android:title="@string/menu_home_sources"
android:orderInCategory="97"
app:showAsAction="never"/>
<item android:id="@+id/action_settings"
android:title="@string/title_activity_settings"
android:orderInCategory="98"

View File

@ -126,4 +126,6 @@
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -126,4 +126,6 @@
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -126,4 +126,6 @@
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -126,4 +126,6 @@
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -126,4 +126,6 @@
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -126,4 +126,6 @@
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -126,4 +126,6 @@
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -126,4 +126,6 @@
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -126,4 +126,6 @@
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -6,4 +6,6 @@
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -126,4 +126,6 @@
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -126,4 +126,6 @@
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -126,4 +126,6 @@
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -126,4 +126,6 @@
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -126,4 +126,6 @@
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -126,4 +126,6 @@
<string name="pref_switch_disable_acra">"禁用自动错误报告 "</string>
<string name="menu_home_filter">筛选器</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -126,4 +126,6 @@
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -129,4 +129,6 @@
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
</resources>

View File

@ -231,7 +231,8 @@ class RepositoryTest {
listOf("tags"),
"spouts\\rss\\fulltextrss",
"",
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png",
SelfossModel.SourceParams("url")
))
runBlocking {
repository.getNewerItems()
@ -551,7 +552,8 @@ class RepositoryTest {
listOf("Test", "second"),
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
"d8c92cdb1ef119ea85c4b9205c879ca7.png",
SelfossModel.SourceParams("url")
),
SelfossModel.Source(
2,
@ -559,7 +561,8 @@ class RepositoryTest {
listOf("second"),
"spouts\\rss\\fulltextrss",
"",
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png",
SelfossModel.SourceParams("url")
)
)
val sourcesDB = listOf(
@ -707,7 +710,7 @@ class RepositoryTest {
@Test
fun create_source() {
coEvery { api.createSourceForVersion(any(), any(), any(), any(), any()) } returns
coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns
SuccessResponse(true)
initializeRepository()
@ -718,7 +721,6 @@ class RepositoryTest {
"https://test.com/feed",
"spouts\\rss\\fulltextrss",
"Test, New",
""
)
}
@ -728,7 +730,6 @@ class RepositoryTest {
any(),
any(),
any(),
any(),
)
}
assertSame(true, response)
@ -736,7 +737,7 @@ class RepositoryTest {
@Test
fun create_source_but_response_fails() {
coEvery { api.createSourceForVersion(any(), any(), any(), any(), any()) } returns
coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns
SuccessResponse(false)
initializeRepository()
@ -746,8 +747,7 @@ class RepositoryTest {
"test",
"https://test.com/feed",
"spouts\\rss\\fulltextrss",
"Test, New",
""
"Test, New"
)
}
@ -756,8 +756,7 @@ class RepositoryTest {
any(),
any(),
any(),
any(),
any(),
any()
)
}
assertSame(false, response)
@ -765,7 +764,7 @@ class RepositoryTest {
@Test
fun create_source_without_connection() {
coEvery { api.createSourceForVersion(any(), any(), any(), any(), any()) } returns
coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns
SuccessResponse(true)
initializeRepository(MutableStateFlow(false))
@ -775,8 +774,7 @@ class RepositoryTest {
"test",
"https://test.com/feed",
"spouts\\rss\\fulltextrss",
"Test, New",
""
"Test, New"
)
}
@ -786,7 +784,6 @@ class RepositoryTest {
any(),
any(),
any(),
any()
)
}
assertSame(false, response)
@ -1034,7 +1031,8 @@ class RepositoryTest {
listOf("Test", "second"),
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
"d8c92cdb1ef119ea85c4b9205c879ca7.png",
SelfossModel.SourceParams("url")
)
)
repository.searchFilter = "search"

View File

@ -9,6 +9,7 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.encoding.encodeCollection
import kotlinx.serialization.json.*
class SelfossModel {
@ -55,7 +56,12 @@ class SelfossModel {
val tags: List<String>,
val spout: String,
val error: String,
val icon: String?
val icon: String?,
val params: SourceParams?
)
@Serializable
data class SourceParams(
val url: String
)
@Serializable
data class Item(
@ -73,7 +79,7 @@ class SelfossModel {
val sourcetitle: String,
@Serializable(with = TagsListSerializer::class)
val tags: List<String>,
val author: String
val author: String?
) {
// TODO: maybe find a better way to handle these kind of urls
fun getLinkDecoded(): String {
@ -104,7 +110,7 @@ class SelfossModel {
fun sourceAuthorAndDate(): String {
var txt = this.sourcetitle.getHtmlDecoded()
if (this.author.isNotEmpty()) {
if (!this.author.isNullOrBlank()) {
txt += " (by ${this.author}) "
}
txt += DateUtils.parseRelativeDate(this.datetime)
@ -122,7 +128,7 @@ class SelfossModel {
object TagsListSerializer : KSerializer<List<String>> {
override fun deserialize(decoder: Decoder): List<String> {
return when(val json = ((decoder as JsonDecoder).decodeJsonElement())) {
is JsonArray -> json.toList().map { it.toString() }
is JsonArray -> json.toList().map { it.toString().replace("^\"|\"$".toRegex(), "") }
else -> json.toString().split(",")
}
@ -132,7 +138,7 @@ class SelfossModel {
get() = PrimitiveSerialDescriptor("tags", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: List<String>) {
TODO("Not yet implemented")
encoder.encodeCollection(PrimitiveSerialDescriptor("tags", PrimitiveKind.STRING), value.size) { this.toString() }
}
}

View File

@ -48,6 +48,7 @@ class Repository(
private var fetchedTags = false
private var _readerItems = ArrayList<SelfossModel.Item>()
private var _selectedSource: SelfossModel.Source? = null
suspend fun getNewerItems(): ArrayList<SelfossModel.Item> {
// TODO: Use the updatedSince parameter
@ -343,7 +344,6 @@ class Repository(
url: String,
spout: String,
tags: String,
filter: String
): Boolean {
var response = false
if (isNetworkAvailable()) {
@ -352,13 +352,27 @@ class Repository(
url,
spout,
tags,
filter
).isSuccess == true
}
return response
}
suspend fun updateSource(
id: Int,
title: String,
url: String,
spout: String,
tags: String
): Boolean {
var response = false
if (isNetworkAvailable()) {
response = api.updateSourceForVersion(id, title, url, spout, tags).isSuccess == true
}
return response
}
suspend fun deleteSource(id: Int, title: String): Boolean {
var success = false
if (isNetworkAvailable()) {
@ -511,6 +525,7 @@ class Repository(
item.link,
item.sourcetitle,
item.tags.joinToString(","),
item.author,
item.id.toString()
)
@ -575,4 +590,20 @@ class Repository(
fun getReaderItems(): ArrayList<SelfossModel.Item> {
return _readerItems
}
fun migrate(driverFactory: DriverFactory) {
ReaderForSelfossDB.Schema.migrate(driverFactory.createDriver(), 0, 1)
}
fun setSelectedSource(source: SelfossModel.Source) {
_selectedSource = source
}
fun unsetSelectedSource() {
_selectedSource = null
}
fun getSelectedSource(): SelfossModel.Source? {
return _selectedSource
}
}

View File

@ -23,6 +23,9 @@ suspend fun maybeResponse(r: HttpResponse?): SuccessResponse {
return if (r != null && r.status.isSuccess()) {
r.body()
} else {
if (r != null) {
Napier.i("Error ${r.status}", tag = "maybeResponse")
}
SuccessResponse(false)
}
}

View File

@ -76,10 +76,14 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
// Api version was introduces after the POST login, so when there is a version, it should be available
private fun shouldHavePostLogin() = appSettingsService.getApiVersion() != -1
private fun hasLoginInfo() = appSettingsService.getUserName().isNotEmpty() && appSettingsService.getPassword().isNotEmpty()
private fun hasLoginInfo() =
appSettingsService.getUserName().isNotEmpty() && appSettingsService.getPassword()
.isNotEmpty()
suspend fun login(): SuccessResponse =
if (appSettingsService.getUserName().isNotEmpty() && appSettingsService.getPassword().isNotEmpty()) {
if (appSettingsService.getUserName().isNotEmpty() && appSettingsService.getPassword()
.isNotEmpty()
) {
if (shouldHavePostLogin()) {
postLogin()
} else {
@ -99,7 +103,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("password", appSettingsService.getPassword())
})
private fun shouldHaveNewLogout() = appSettingsService.getApiVersion() >= 5 // We are missing 4.1.0
private fun shouldHaveNewLogout() =
appSettingsService.getApiVersion() >= 5 // We are missing 4.1.0
suspend fun logout(): SuccessResponse =
if (shouldHaveNewLogout()) {
doLogout()
@ -107,7 +113,8 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
maybeLogoutIfAvailable()
}
private suspend fun maybeLogoutIfAvailable() = responseOrSuccessIf404(client.tryToGet(url("/logout")))
private suspend fun maybeLogoutIfAvailable() =
responseOrSuccessIf404(client.tryToGet(url("/logout")))
private suspend fun doLogout() = maybeResponse(client.tryToDelete(url("/api/session/current")))
@ -236,13 +243,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
url: String,
spout: String,
tags: String,
filter: String
): SuccessResponse =
maybeResponse(
if (appSettingsService.getApiVersion() > 1) {
createSource("tags[]", title, url, spout, tags, filter)
createSource("tags[]", title, url, spout, tags)
} else {
createSource("tags", title, url, spout, tags, filter)
createSource("tags", title, url, spout, tags)
}
)
@ -251,8 +257,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
title: String,
url: String,
spout: String,
tags: String,
filter: String
tags: String
): HttpResponse? =
client.tryToSubmitForm(
url = url("/source"),
@ -265,7 +270,43 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
append("url", url)
append("spout", spout)
append(tagsParamName, tags)
append("filter", filter)
}
)
suspend fun updateSourceForVersion(
id: Int,
title: String,
url: String,
spout: String,
tags: String
): SuccessResponse =
maybeResponse(
if (appSettingsService.getApiVersion() > 1) {
updateSource(id, "tags[]", title, url, spout, tags)
} else {
updateSource(id, "tags", title, url, spout, tags)
}
)
private suspend fun updateSource(
id: Int,
tagsParamName: String,
title: String,
url: String,
spout: String,
tags: String,
): HttpResponse? =
client.tryToSubmitForm(
url = url("/source/$id"),
formParameters = Parameters.build {
if (!shouldHavePostLogin()) {
append("username", appSettingsService.getUserName())
append("password", appSettingsService.getPassword())
}
append("title", title)
append("url", url)
append("spout", spout)
append(tagsParamName, tags)
}
)

View File

@ -19,7 +19,8 @@ fun SOURCE.toView(): SelfossModel.Source =
this.tags.split(","),
this.spout,
this.error,
this.icon
this.icon,
if (this.url != null) SelfossModel.SourceParams(this.url) else null
)
fun SelfossModel.Source.toEntity(): SOURCE =
@ -29,7 +30,8 @@ fun SelfossModel.Source.toEntity(): SOURCE =
this.tags.joinToString(","),
this.spout,
this.error,
this.icon.orEmpty()
this.icon.orEmpty(),
this.params?.url
)
fun SelfossModel.Tag.toEntity(): TAG =

View File

@ -1 +1,6 @@
ALTER TABLE ITEM ADD COLUMN `author` TEXT NOT NULL;
CREATE TABLE ITEM_BACKUP AS SELECT `id`, `datetime`, `title`, `content`,
`unread`, `starred`, `thumbnail`, `icon`, `link`, `sourcetitle`,
`tags` FROM ITEM;
ALTER TABLE ITEM_BACKUP ADD COLUMN `author` TEXT;
DROP TABLE ITEM;
ALTER TABLE ITEM_BACKUP RENAME TO ITEM;

View File

@ -0,0 +1 @@
ALTER TABLE SOURCE ADD COLUMN `url` TEXT;

View File

@ -10,7 +10,7 @@ CREATE TABLE ITEM (
`link` TEXT NOT NULL,
`sourcetitle` TEXT NOT NULL,
`tags` TEXT NOT NULL,
`author` TEXT NOT NULL,
`author` TEXT,
PRIMARY KEY(`id`)
);
@ -31,4 +31,4 @@ deleteItemsWhereSource:
DELETE FROM ITEM WHERE `sourcetitle` = ?;
updateItem:
UPDATE ITEM SET `datetime` = ?, `title` = ?, `content` = ?, `unread` = ?, `starred` = ?, `thumbnail` = ?, `icon` = ?, `link` = ?, `sourcetitle` = ?, `tags` = ? WHERE `id` = ?;
UPDATE ITEM SET `datetime` = ?, `title` = ?, `content` = ?, `unread` = ?, `starred` = ?, `thumbnail` = ?, `icon` = ?, `link` = ?, `sourcetitle` = ?, `tags` = ?, `author` = ? WHERE `id` = ?;

View File

@ -5,6 +5,7 @@ CREATE TABLE SOURCE (
`spout` TEXT NOT NULL,
`error` TEXT NOT NULL,
`icon` TEXT NOT NULL,
`url` TEXT,
PRIMARY KEY(`id`)
);