Compare commits

..

No commits in common. "master" and "v123113311" have entirely different histories.

14 changed files with 141 additions and 238 deletions

View File

@ -1,63 +1,3 @@
**v124041081**
- chore: comment.
- fix: Last time fixing the parsing date hack before moving it to os version.
- Changelog for v124030731 [CI SKIP]
--------------------------------------------------------------------
**v124030731**
- fix: Basic auth and password can have non whitspace characters. Fixes 142.
- Changelog for v124020451 [CI SKIP]
--------------------------------------------------------------------
**v124020451**
- fix: Fixed handling of position in card adapter.
- Changelog for v124010301 [CI SKIP]
--------------------------------------------------------------------
**v124010301**
- fix: This may fix the oom errors.
- Changelog for v124010191 [CI SKIP]
--------------------------------------------------------------------
**v124010191**
- fix: moving listeners.
- chore: removed a useless log.
- Changelog for v124010032 [CI SKIP]
--------------------------------------------------------------------
**v124010032**
- fix: Another date format thing.
- Changelog for v124010031 [CI SKIP]
--------------------------------------------------------------------
**v124010031**
- fix: Checking if selfoss instance.
- fix: handle three characters lenght hexcode colors.
- Changelog for v123113311 [CI SKIP]
--------------------------------------------------------------------
**v123113311**
- chore: Source tracker url in the menu.
- fix: Handle kodein proguard rules.
- Changelog for v123102961 [CI SKIP]
--------------------------------------------------------------------
**v123102961** **v123102961**
- chore: domain changes. - chore: domain changes.

View File

@ -140,25 +140,20 @@ class LoginActivity : AppCompatActivity(), DIAware {
repository.refreshLoginInformation(url, login, password) repository.refreshLoginInformation(url, login, password)
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
try {
repository.updateApiInformation() repository.updateApiInformation()
} catch (e: Exception) { val result = repository.login()
if (e.message?.startsWith("No transformation found") == true) { if (result) {
val (errorFetching, displaySelfossOnly) = repository.shouldBeSelfossInstance()
if (!errorFetching && !displaySelfossOnly) {
goToMain()
} else {
if (displaySelfossOnly) {
Toast.makeText( Toast.makeText(
applicationContext, applicationContext,
R.string.application_selfoss_only, R.string.application_selfoss_only,
Toast.LENGTH_LONG, Toast.LENGTH_LONG,
).show() ).show()
preferenceError()
showProgress(false)
} }
}
val result = repository.login()
if (result) {
val errorFetching = repository.checkIfFetchFails()
if (!errorFetching) {
goToMain()
} else {
preferenceError() preferenceError()
} }
} else { } else {

View File

@ -9,7 +9,6 @@ import android.widget.ImageView.ScaleType
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.databinding.CardItemBinding import bou.amine.apps.readerforselfossv2.android.databinding.CardItemBinding
import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName
import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener
import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularDrawable import bou.amine.apps.readerforselfossv2.android.utils.glide.circularDrawable
@ -35,7 +34,6 @@ class ItemCardAdapter(
override var items: ArrayList<SelfossModel.Item>, override var items: ArrayList<SelfossModel.Item>,
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit, override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit,
) : ItemsAdapter<ItemCardAdapter.ViewHolder>() { ) : ItemsAdapter<ItemCardAdapter.ViewHolder>() {
private lateinit var binding: CardItemBinding
private val c: Context = app.baseContext private val c: Context = app.baseContext
private val imageMaxHeight: Int = private val imageMaxHeight: Int =
c.resources.getDimension(R.dimen.card_image_max_height).toInt() c.resources.getDimension(R.dimen.card_image_max_height).toInt()
@ -48,57 +46,16 @@ class ItemCardAdapter(
parent: ViewGroup, parent: ViewGroup,
viewType: Int, viewType: Int,
): ViewHolder { ): ViewHolder {
binding = CardItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) val binding = CardItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding) return ViewHolder(binding)
} }
private fun handleClickListeners(position: Int) {
binding.favButton.setOnClickListener {
val item = items[position]
if (item.starred) {
CoroutineScope(Dispatchers.IO).launch {
repository.unstarr(item)
}
binding.favButton.isSelected = false
} else {
CoroutineScope(Dispatchers.IO).launch {
repository.starr(item)
}
binding.favButton.isSelected = true
}
}
binding.shareBtn.setOnClickListener {
val item = items[position]
c.shareLink(item.getLinkDecoded(), item.title.getHtmlDecoded())
}
binding.browserBtn.setOnClickListener {
c.openInBrowserAsNewTask(items[position])
}
}
private fun handleLinkOpening(position: Int) {
binding.root.setOnClickListener {
repository.setReaderItems(items)
c.openItemUrl(
position,
items[position].getLinkDecoded(),
appSettingsService.isArticleViewerEnabled(),
app,
)
}
}
override fun onBindViewHolder( override fun onBindViewHolder(
holder: ViewHolder, holder: ViewHolder,
position: Int, position: Int,
) { ) {
with(holder) { with(holder) {
val itm = items[holder.bindingAdapterPosition] val itm = items[position]
handleClickListeners(holder.bindingAdapterPosition)
handleLinkOpening(holder.bindingAdapterPosition)
binding.favButton.isSelected = itm.starred binding.favButton.isSelected = itm.starred
if (appSettingsService.getPublicAccess()) { if (appSettingsService.getPublicAccess()) {
@ -111,12 +68,7 @@ class ItemCardAdapter(
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent)) binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
binding.sourceTitleAndDate.text = try { binding.sourceTitleAndDate.text = itm.sourceAuthorAndDate()
itm.sourceAuthorAndDate()
} catch (e: Exception) {
e.sendSilentlyWithAcraWithName("ItemCardAdapter parse date")
itm.sourceAuthorOnly()
}
if (!appSettingsService.isFullHeightCardsEnabled()) { if (!appSettingsService.isFullHeightCardsEnabled()) {
binding.itemImage.maxHeight = imageMaxHeight binding.itemImage.maxHeight = imageMaxHeight
@ -144,5 +96,48 @@ class ItemCardAdapter(
return items.size return items.size
} }
inner class ViewHolder(val binding: CardItemBinding) : RecyclerView.ViewHolder(binding.root) inner class ViewHolder(val binding: CardItemBinding) : RecyclerView.ViewHolder(binding.root) {
init {
handleClickListeners()
handleLinkOpening()
}
private fun handleClickListeners() {
binding.favButton.setOnClickListener {
val item = items[bindingAdapterPosition]
if (item.starred) {
CoroutineScope(Dispatchers.IO).launch {
repository.unstarr(item)
}
binding.favButton.isSelected = false
} else {
CoroutineScope(Dispatchers.IO).launch {
repository.starr(item)
}
binding.favButton.isSelected = true
}
}
binding.shareBtn.setOnClickListener {
val item = items[bindingAdapterPosition]
c.shareLink(item.getLinkDecoded(), item.title.getHtmlDecoded())
}
binding.browserBtn.setOnClickListener {
c.openInBrowserAsNewTask(items[bindingAdapterPosition])
}
}
private fun handleLinkOpening() {
binding.root.setOnClickListener {
repository.setReaderItems(items)
c.openItemUrl(
bindingAdapterPosition,
items[bindingAdapterPosition].getLinkDecoded(),
appSettingsService.isArticleViewerEnabled(),
app,
)
}
}
}
} }

View File

@ -7,7 +7,6 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.databinding.ListItemBinding import bou.amine.apps.readerforselfossv2.android.databinding.ListItemBinding
import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName
import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularDrawable import bou.amine.apps.readerforselfossv2.android.utils.glide.circularDrawable
import bou.amine.apps.readerforselfossv2.android.utils.openItemUrl import bou.amine.apps.readerforselfossv2.android.utils.openItemUrl
@ -26,7 +25,6 @@ class ItemListAdapter(
override var items: ArrayList<SelfossModel.Item>, override var items: ArrayList<SelfossModel.Item>,
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit, override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit,
) : ItemsAdapter<ItemListAdapter.ViewHolder>() { ) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
private lateinit var binding: ListItemBinding
private val c: Context = app.baseContext private val c: Context = app.baseContext
override val di: DI by closestDI(app) override val di: DI by closestDI(app)
@ -37,7 +35,7 @@ class ItemListAdapter(
parent: ViewGroup, parent: ViewGroup,
viewType: Int, viewType: Int,
): ViewHolder { ): ViewHolder {
binding = ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) val binding = ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding) return ViewHolder(binding)
} }
@ -46,17 +44,7 @@ class ItemListAdapter(
position: Int, position: Int,
) { ) {
with(holder) { with(holder) {
val itm = items[holder.bindingAdapterPosition] val itm = items[position]
binding.root.setOnClickListener {
repository.setReaderItems(items)
c.openItemUrl(
holder.bindingAdapterPosition,
items[holder.bindingAdapterPosition].getLinkDecoded(),
appSettingsService.isArticleViewerEnabled(),
app,
)
}
binding.title.text = itm.title.getHtmlDecoded() binding.title.text = itm.title.getHtmlDecoded()
@ -64,12 +52,7 @@ class ItemListAdapter(
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent)) binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
binding.sourceTitleAndDate.text = try { binding.sourceTitleAndDate.text = itm.sourceAuthorAndDate()
itm.sourceAuthorAndDate()
} catch (e: Exception) {
e.sendSilentlyWithAcraWithName("ItemListAdapter parse date")
itm.sourceAuthorOnly()
}
if (itm.getThumbnail(repository.baseUrl).isEmpty()) { if (itm.getThumbnail(repository.baseUrl).isEmpty()) {
if (itm.getIcon(repository.baseUrl).isEmpty()) { if (itm.getIcon(repository.baseUrl).isEmpty()) {
@ -85,5 +68,21 @@ class ItemListAdapter(
override fun getItemCount(): Int = items.size override fun getItemCount(): Int = items.size
inner class ViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root) inner class ViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root) {
init {
handleLinkOpening()
}
private fun handleLinkOpening() {
binding.root.setOnClickListener {
repository.setReaderItems(items)
c.openItemUrl(
bindingAdapterPosition,
items[bindingAdapterPosition].getLinkDecoded(),
appSettingsService.isArticleViewerEnabled(),
app,
)
}
}
}
} }

View File

@ -89,7 +89,6 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
if (repository.displayedItems == ItemType.UNREAD) { if (repository.displayedItems == ItemType.UNREAD) {
items.remove(item) items.remove(item)
notifyItemRemoved(position) notifyItemRemoved(position)
notifyItemRangeChanged(position, itemCount)
updateItems(items) updateItems(items)
} else { } else {
notifyItemChanged(position) notifyItemChanged(position)

View File

@ -50,33 +50,6 @@ class SourcesListAdapter(
) { ) {
val itm = items[position] val itm = items[position]
val deleteBtn: Button = holder.mView.findViewById(R.id.deleteBtn)
deleteBtn.setOnClickListener {
val (id, title) = items[position]
CoroutineScope(Dispatchers.IO).launch {
val successfullyDeletedSource = repository.deleteSource(id, title)
if (successfullyDeletedSource) {
items.removeAt(position)
notifyItemRemoved(position)
notifyItemRangeChanged(position, itemCount)
} else {
Toast.makeText(
app,
R.string.can_delete_source,
Toast.LENGTH_SHORT,
).show()
}
}
}
holder.mView.setOnClickListener {
val source = items[position]
repository.setSelectedSource(source)
app.startActivity(Intent(app, UpsertSourceActivity::class.java))
}
if (itm.getIcon(repository.baseUrl).isEmpty()) { if (itm.getIcon(repository.baseUrl).isEmpty()) {
binding.itemImage.setBackgroundAndText(itm.title.getHtmlDecoded()) binding.itemImage.setBackgroundAndText(itm.title.getHtmlDecoded())
} else { } else {
@ -99,5 +72,38 @@ class SourcesListAdapter(
override fun getItemCount(): Int = items.size override fun getItemCount(): Int = items.size
inner class ViewHolder(val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) inner class ViewHolder(private val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) {
init {
handleClickListeners()
}
private fun handleClickListeners() {
val deleteBtn: Button = mView.findViewById(R.id.deleteBtn)
deleteBtn.setOnClickListener {
val (id, title) = items[bindingAdapterPosition]
CoroutineScope(Dispatchers.IO).launch {
val successfullyDeletedSource = repository.deleteSource(id, title)
if (successfullyDeletedSource) {
items.removeAt(bindingAdapterPosition)
notifyItemRemoved(bindingAdapterPosition)
notifyItemRangeChanged(bindingAdapterPosition, itemCount)
} else {
Toast.makeText(
app,
R.string.can_delete_source,
Toast.LENGTH_SHORT,
).show()
}
}
}
mView.setOnClickListener {
val source = items[bindingAdapterPosition]
repository.setSelectedSource(source)
app.startActivity(Intent(app, UpsertSourceActivity::class.java))
}
}
}
} }

View File

@ -52,6 +52,7 @@ import org.kodein.di.DIAware
import org.kodein.di.android.x.closestDI import org.kodein.di.android.x.closestDI
import org.kodein.di.instance import org.kodein.di.instance
import java.net.MalformedURLException import java.net.MalformedURLException
import java.net.SocketTimeoutException
import java.net.URL import java.net.URL
import java.util.* import java.util.*
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
@ -102,12 +103,7 @@ class ArticleFragment : Fragment(), DIAware {
contentText = item.content contentText = item.content
contentTitle = item.title.getHtmlDecoded() contentTitle = item.title.getHtmlDecoded()
contentImage = item.getThumbnail(repository.baseUrl) contentImage = item.getThumbnail(repository.baseUrl)
contentSource = try { contentSource = item.sourceAuthorAndDate()
item.sourceAuthorAndDate()
} catch (e: Exception) {
e.sendSilentlyWithAcraWithName("Article Fragment parse date")
item.sourceAuthorOnly()
}
allImages = item.getImages() allImages = item.getImages()
fontSize = appSettingsService.getFontSize() fontSize = appSettingsService.getFontSize()
@ -268,7 +264,10 @@ class ArticleFragment : Fragment(), DIAware {
} else { } else {
openInBrowserAfterFailing() openInBrowserAfterFailing()
} }
} catch (e: SocketTimeoutException) {
openInBrowserAfterFailing()
} catch (e: Exception) { } catch (e: Exception) {
e.sendSilentlyWithAcraWithName("getContentFromMercury > $url")
openInBrowserAfterFailing() openInBrowserAfterFailing()
} }
} }

View File

@ -17,7 +17,6 @@ import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.databinding.FilterFragmentBinding import bou.amine.apps.readerforselfossv2.android.databinding.FilterFragmentBinding
import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.utils.getColorHexCode
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
import bou.amine.apps.readerforselfossv2.utils.getIcon import bou.amine.apps.readerforselfossv2.utils.getIcon
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
@ -148,9 +147,9 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
val gd = GradientDrawable() val gd = GradientDrawable()
val gdColor = val gdColor =
try { try {
Color.parseColor(tag.getColorHexCode()) Color.parseColor(tag.color)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
e.sendSilentlyWithAcraWithName("color issue " + tag.color + " / " + tag.getColorHexCode()) e.sendSilentlyWithAcraWithName("color issue " + tag.color)
resources.getColor(R.color.colorPrimary) resources.getColor(R.color.colorPrimary)
} }
gd.setColor(gdColor) gd.setColor(gdColor)

View File

@ -10,11 +10,9 @@ import org.junit.Test
class DatesTest { class DatesTest {
private val newVersionDateVariant = "2022-12-24T17:00:08+00" private val newVersionDateVariant = "2022-12-24T17:00:08+00"
private val newVersionDate = "2013-04-07T13:43:00+01:00" private val newVersionDate = "2013-04-07T13:43:00+01:00"
private val newVersionDate2 = "2013-04-07T13:43:00-01:00"
private val oldVersionDate = "2013-05-07 13:46:00" private val oldVersionDate = "2013-05-07 13:46:00"
private val oldVersionDateVariant = "2021-03-21 10:32:00.000000" private val oldVersionDateVariant = "2021-03-21 10:32:00.000000"
@Test @Test
fun new_version_date_should_be_parsed() { fun new_version_date_should_be_parsed() {
val date = DateUtils.parseDate(newVersionDate) val date = DateUtils.parseDate(newVersionDate)
@ -24,15 +22,6 @@ class DatesTest {
assertEquals(expected, date) assertEquals(expected, date)
} }
@Test
fun new_version_date2_should_be_parsed() {
val date = DateUtils.parseDate(newVersionDate2)
val expected =
LocalDateTime(2013, 4, 7, 13, 43, 0, 0).toInstant(TimeZone.currentSystemDefault())
.toEpochMilliseconds()
assertEquals(expected, date)
}
@Test @Test
fun old_version_date_should_be_parsed() { fun old_version_date_should_be_parsed() {

View File

@ -1,7 +1,6 @@
package bou.amine.apps.readerforselfossv2.utils package bou.amine.apps.readerforselfossv2.utils
import android.text.format.DateUtils import android.text.format.DateUtils
import io.github.aakira.napier.Napier
import kotlinx.datetime.* import kotlinx.datetime.*
actual class DateUtils { actual class DateUtils {
@ -11,22 +10,19 @@ actual class DateUtils {
private val oldVersionFormat = "\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}(.()\\d*)?".toRegex() private val oldVersionFormat = "\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}(.()\\d*)?".toRegex()
// yyyy-MM-dd'T'HH:mm:ss[.SSS]XXX (RFC3339) // yyyy-MM-dd'T'HH:mm:ss[.SSS]XXX (RFC3339)
private val newVersionFormat = "(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2})[+-](\\d{2}(:\\d{2})?)?".toRegex() private val newVersionFormat = "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\+\\d{2}(:\\d{2})?".toRegex()
// TODO: do not fix any more issues here. Move everything to plateform specific code. // We may need to consider moving the formatting to platform specific code, even if the tests are doubled
// For now, we handle this in a hacky way, because kotlin only accepts iso formats
actual fun parseDate(dateString: String): Long { actual fun parseDate(dateString: String): Long {
var isoDateString: String = var isoDateString: String =
try {
if (dateString.matches(oldVersionFormat)) { if (dateString.matches(oldVersionFormat)) {
dateString.replace(" ", "T") dateString.replace(" ", "T")
} else if (dateString.matches(newVersionFormat)) { } else if (dateString.matches(newVersionFormat)) {
newVersionFormat.find(dateString)?.groups?.get(1)?.value ?: throw Exception("Couldn't parse $dateString") dateString.split("+")[0]
} else { } else {
throw Exception("Unrecognized format for $dateString") throw Exception("Unrecognized format for $dateString")
} }
} catch (e: Exception) {
throw Exception("parseDate failed for $dateString", e)
}
return LocalDateTime.parse(isoDateString).toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds() return LocalDateTime.parse(isoDateString).toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds()
} }

View File

@ -146,14 +146,6 @@ class SelfossModel {
return txt return txt
} }
fun sourceAuthorOnly(): String {
var txt = this.sourcetitle.getHtmlDecoded()
if (!this.author.isNullOrBlank()) {
txt += " (by ${this.author}) "
}
return txt
}
fun toggleStar(): Item { fun toggleStar(): Item {
this.starred = !this.starred this.starred = !this.starred
return this return this

View File

@ -8,6 +8,7 @@ import bou.amine.apps.readerforselfossv2.rest.SelfossApi
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import bou.amine.apps.readerforselfossv2.utils.* import bou.amine.apps.readerforselfossv2.utils.*
import io.github.aakira.napier.Napier import io.github.aakira.napier.Napier
import io.ktor.client.call.*
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -422,25 +423,28 @@ class Repository(
val response = api.login() val response = api.login()
result = response.isSuccess == true result = response.isSuccess == true
} catch (cause: Throwable) { } catch (cause: Throwable) {
Napier.e("login failed", cause, tag = "RepositoryImpl.login") Napier.e(cause.stackTraceToString(), tag = "RepositoryImpl.login")
} }
} }
return result return result
} }
suspend fun checkIfFetchFails(): Boolean { suspend fun shouldBeSelfossInstance(): Pair<Boolean, Boolean> {
var fetchFailed = true var fetchFailed = true
var showSelfossOnlyModal = false
if (isNetworkAvailable()) { if (isNetworkAvailable()) {
try { try {
// Trying to fetch one item, and check someone is trying to use the app with // Trying to fetch one item, and check someone is trying to use the app with
// a random rss feed, that would throw a NoTransformationFoundException // a random rss feed, that would throw a NoTransformationFoundException
fetchFailed = !api.getItemsWithoutCatch().success fetchFailed = !api.getItemsWithoutCatch().success
} catch (e: NoTransformationFoundException) {
showSelfossOnlyModal = true
} catch (e: Throwable) { } catch (e: Throwable) {
Napier.e("checkIfFetchFails failed", e, tag = "RepositoryImpl.shouldBeSelfossInstance") Napier.e(e.stackTraceToString(), tag = "RepositoryImpl.shouldBeSelfossInstance")
} }
} }
return fetchFailed return Pair(fetchFailed, showSelfossOnlyModal)
} }
suspend fun logout() { suspend fun logout() {
@ -451,7 +455,7 @@ class Repository(
Napier.e("Couldn't logout.", tag = "RepositoryImpl.logout") Napier.e("Couldn't logout.", tag = "RepositoryImpl.logout")
} }
} catch (cause: Throwable) { } catch (cause: Throwable) {
Napier.e("logout failed", cause, tag = "RepositoryImpl.logout") Napier.e(cause.stackTraceToString(), tag = "RepositoryImpl.logout")
} }
appSettingsService.clearAll() appSettingsService.clearAll()
} else { } else {

View File

@ -441,7 +441,7 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
login: String, login: String,
password: String, password: String,
) { ) {
val regex = """\/\/(\S+):(\S+)@""".toRegex() val regex = """\/\/(\D+):(\D+)@""".toRegex()
val matchResult = regex.find(url) val matchResult = regex.find(url)
if (matchResult != null) { if (matchResult != null) {
val (basicLogin, basicPassword) = matchResult.destructured val (basicLogin, basicPassword) = matchResult.destructured

View File

@ -73,13 +73,3 @@ fun SelfossModel.Item.toEntity(): ITEM =
this.tags.joinToString(","), this.tags.joinToString(","),
this.author, this.author,
) )
fun SelfossModel.Tag.getColorHexCode(): String =
if (this.color.length == 4) { // #000
val char1 = this.color.get(1)
val char2 = this.color.get(2)
val char3 = this.color.get(3)
"#$char1$char1$char2$char2$char3$char3"
} else {
this.color
}