Implement FreshRSS add/update/delete feed actions

This commit is contained in:
Shinokuni 2024-05-20 22:07:30 +02:00
parent 940b6de97a
commit 133d8e6992
6 changed files with 118 additions and 42 deletions

View File

@ -121,21 +121,29 @@ class FeedScreenModel(
_feedState.update { it.copy(areFoldersExpanded = isExpanded) }
fun closeDialog(dialog: DialogState? = null) {
if (dialog is DialogState.AddFeed) {
_addFeedDialogState.update {
it.copy(
url = "",
error = null,
)
when (dialog) {
is DialogState.AddFeed -> {
_addFeedDialogState.update {
it.copy(
url = "",
error = null,
exception = null
)
}
}
} else if (dialog is DialogState.AddFolder || dialog is DialogState.UpdateFolder) {
_folderState.update {
it.copy(
folder = Folder(),
nameError = null,
exception = null
)
is DialogState.AddFolder, is DialogState.UpdateFolder -> {
_folderState.update {
it.copy(
folder = Folder(),
nameError = null,
exception = null
)
}
}
is DialogState.UpdateFeed -> {
_updateFeedDialogState.update { it.copy(exception = null) }
}
else -> {}
}
_feedState.update { it.copy(dialog = null) }
@ -166,7 +174,11 @@ class FeedScreenModel(
fun deleteFeed(feed: Feed) {
screenModelScope.launch(Dispatchers.IO) {
repository?.deleteFeed(feed)
try {
repository?.deleteFeed(feed)
} catch (e: Exception) {
_feedState.update { it.copy(exception = e) }
}
}
}
@ -180,7 +192,7 @@ class FeedScreenModel(
}
}
// Add feed
//region Add Feed
fun setAddFeedDialogURL(url: String) {
_addFeedDialogState.update {
@ -218,12 +230,7 @@ class FeedScreenModel(
else -> screenModelScope.launch(Dispatchers.IO) {
try {
if (localRSSDataSource.isUrlRSSResource(url)) {
// TODO add support for all account types
repository?.insertNewFeeds(
newFeeds = listOf(Feed(url = url))
) {}
closeDialog(DialogState.AddFeed)
insertFeeds(listOf(Feed(url = url)))
} else {
val rssUrls = HtmlParser.getFeedLink(url, get())
@ -232,12 +239,7 @@ class FeedScreenModel(
it.copy(error = TextFieldError.NoRSSFeed)
}
} else {
// TODO add support for all account types
repository?.insertNewFeeds(
newFeeds = rssUrls.map { Feed(url = it.url) }
) {}
closeDialog(DialogState.AddFeed)
insertFeeds(rssUrls.map { Feed(url = it.url) })
}
}
} catch (e: Exception) {
@ -250,9 +252,22 @@ class FeedScreenModel(
}
}
// add feed
private suspend fun insertFeeds(feeds: List<Feed>) {
val errors = repository?.insertNewFeeds(
newFeeds = feeds,
onUpdate = { /* no need of this here */ }
)!!
// update feed
if (errors.isEmpty()) {
closeDialog(DialogState.AddFeed)
} else {
_addFeedDialogState.update { it.copy(exception = errors.values.first()) }
}
}
//endregion
//region Update feed
fun setAccountDropDownState(isExpanded: Boolean) {
_updateFeedDialogState.update {
@ -311,16 +326,24 @@ class FeedScreenModel(
}
else -> {
_updateFeedDialogState.update { it.copy(exception = null) }
screenModelScope.launch(Dispatchers.IO) {
with(_updateFeedDialogState.value) {
repository?.updateFeed(
Feed(
id = feedId,
name = feedName,
url = feedUrl,
folderId = selectedFolder?.id
try {
repository?.updateFeed(
Feed(
id = feedId,
name = feedName,
url = feedUrl,
folderId = selectedFolder?.id,
remoteFolderId = selectedFolder?.remoteId
)
)
)
} catch (e: Exception) {
_updateFeedDialogState.update { it.copy(exception = e) }
return@launch
}
}
closeDialog()
@ -329,9 +352,9 @@ class FeedScreenModel(
}
}
// update feed
//endregion
// add/update folder
//region Add/Update folder
fun setFolderName(name: String) = _folderState.update {
it.copy(
@ -369,5 +392,5 @@ class FeedScreenModel(
fun resetException() = _feedState.update { it.copy(exception = null) }
// add/update folder
//endregion
}

View File

@ -34,6 +34,7 @@ data class AddFeedDialogState(
val selectedAccount: Account = Account(accountName = ""),
val accounts: List<Account> = listOf(),
val error: TextFieldError? = null,
val exception: Exception? = null
) {
val isError: Boolean get() = error != null
}
@ -48,7 +49,8 @@ data class UpdateFeedDialogState(
val folders: List<Folder> = listOf(),
val isAccountDropDownExpanded: Boolean = false,
val isFeedUrlReadOnly: Boolean = true,
val isNoFolderCase: Boolean = false
val isNoFolderCase: Boolean = false,
val exception: Exception? = null
) {
val isFeedNameError
get() = feedNameError != null

View File

@ -123,7 +123,7 @@ object FeedTab : Tab {
is DialogState.UpdateFeed -> {
UpdateFeedDialog(
viewModel = viewModel,
onDismissRequest = { viewModel.closeDialog() }
onDismissRequest = { viewModel.closeDialog(dialog) }
)
}

View File

@ -10,6 +10,7 @@ import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
@ -19,15 +20,19 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.readrops.app.compose.R
import com.readrops.app.compose.account.selection.adaptiveIconPainterResource
import com.readrops.app.compose.feeds.FeedScreenModel
import com.readrops.app.compose.util.ErrorMessage
import com.readrops.app.compose.util.components.BaseDialog
import com.readrops.app.compose.util.theme.LargeSpacer
import com.readrops.app.compose.util.theme.MediumSpacer
import com.readrops.app.compose.util.theme.ShortSpacer
@OptIn(ExperimentalMaterial3Api::class)
@ -118,6 +123,16 @@ fun AddFeedDialog(
)
}
if (state.exception != null) {
MediumSpacer()
Text(
text = ErrorMessage.get(state.exception!!, LocalContext.current),
color = MaterialTheme.colorScheme.error,
textAlign = TextAlign.Center
)
}
LargeSpacer()
TextButton(

View File

@ -5,17 +5,20 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.readrops.app.compose.R
import com.readrops.app.compose.feeds.FeedScreenModel
import com.readrops.app.compose.util.ErrorMessage
import com.readrops.app.compose.util.components.BaseDialog
import com.readrops.app.compose.util.theme.LargeSpacer
import com.readrops.app.compose.util.theme.MediumSpacer
@ -119,6 +122,15 @@ fun UpdateFeedDialog(
)
}
if (state.exception != null) {
MediumSpacer()
Text(
text = ErrorMessage.get(state.exception!!, LocalContext.current),
color = MaterialTheme.colorScheme.error
)
}
LargeSpacer()
TextButton(

View File

@ -85,7 +85,31 @@ class FreshRSSRepository(
override suspend fun insertNewFeeds(
newFeeds: List<Feed>,
onUpdate: (Feed) -> Unit
): ErrorResult = TODO("Not yet implemented")
): ErrorResult {
val errors = mutableMapOf<Feed, Exception>()
for (newFeed in newFeeds) {
onUpdate(newFeed)
try {
dataSource.createFeed(account.writeToken!!, newFeed.url!!)
} catch (e: Exception) {
errors[newFeed] = e
}
}
return errors
}
override suspend fun updateFeed(feed: Feed) {
dataSource.updateFeed(account.writeToken!!, feed.url!!, feed.name!!, feed.remoteFolderId!!)
super.updateFeed(feed)
}
override suspend fun deleteFeed(feed: Feed) {
dataSource.deleteFeed(account.writeToken!!, feed.url!!)
super.deleteFeed(feed)
}
override suspend fun updateFolder(folder: Folder) {
dataSource.updateFolder(account.writeToken!!, folder.remoteId!!, folder.name!!)