Implement FreshRSS add/update/delete feed actions
This commit is contained in:
parent
940b6de97a
commit
133d8e6992
@ -121,14 +121,17 @@ class FeedScreenModel(
|
|||||||
_feedState.update { it.copy(areFoldersExpanded = isExpanded) }
|
_feedState.update { it.copy(areFoldersExpanded = isExpanded) }
|
||||||
|
|
||||||
fun closeDialog(dialog: DialogState? = null) {
|
fun closeDialog(dialog: DialogState? = null) {
|
||||||
if (dialog is DialogState.AddFeed) {
|
when (dialog) {
|
||||||
|
is DialogState.AddFeed -> {
|
||||||
_addFeedDialogState.update {
|
_addFeedDialogState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
url = "",
|
url = "",
|
||||||
error = null,
|
error = null,
|
||||||
|
exception = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if (dialog is DialogState.AddFolder || dialog is DialogState.UpdateFolder) {
|
}
|
||||||
|
is DialogState.AddFolder, is DialogState.UpdateFolder -> {
|
||||||
_folderState.update {
|
_folderState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
folder = Folder(),
|
folder = Folder(),
|
||||||
@ -137,6 +140,11 @@ class FeedScreenModel(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is DialogState.UpdateFeed -> {
|
||||||
|
_updateFeedDialogState.update { it.copy(exception = null) }
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
|
||||||
_feedState.update { it.copy(dialog = null) }
|
_feedState.update { it.copy(dialog = null) }
|
||||||
}
|
}
|
||||||
@ -166,7 +174,11 @@ class FeedScreenModel(
|
|||||||
|
|
||||||
fun deleteFeed(feed: Feed) {
|
fun deleteFeed(feed: Feed) {
|
||||||
screenModelScope.launch(Dispatchers.IO) {
|
screenModelScope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
repository?.deleteFeed(feed)
|
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) {
|
fun setAddFeedDialogURL(url: String) {
|
||||||
_addFeedDialogState.update {
|
_addFeedDialogState.update {
|
||||||
@ -218,12 +230,7 @@ class FeedScreenModel(
|
|||||||
else -> screenModelScope.launch(Dispatchers.IO) {
|
else -> screenModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
if (localRSSDataSource.isUrlRSSResource(url)) {
|
if (localRSSDataSource.isUrlRSSResource(url)) {
|
||||||
// TODO add support for all account types
|
insertFeeds(listOf(Feed(url = url)))
|
||||||
repository?.insertNewFeeds(
|
|
||||||
newFeeds = listOf(Feed(url = url))
|
|
||||||
) {}
|
|
||||||
|
|
||||||
closeDialog(DialogState.AddFeed)
|
|
||||||
} else {
|
} else {
|
||||||
val rssUrls = HtmlParser.getFeedLink(url, get())
|
val rssUrls = HtmlParser.getFeedLink(url, get())
|
||||||
|
|
||||||
@ -232,12 +239,7 @@ class FeedScreenModel(
|
|||||||
it.copy(error = TextFieldError.NoRSSFeed)
|
it.copy(error = TextFieldError.NoRSSFeed)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO add support for all account types
|
insertFeeds(rssUrls.map { Feed(url = it.url) })
|
||||||
repository?.insertNewFeeds(
|
|
||||||
newFeeds = rssUrls.map { Feed(url = it.url) }
|
|
||||||
) {}
|
|
||||||
|
|
||||||
closeDialog(DialogState.AddFeed)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} 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) {
|
fun setAccountDropDownState(isExpanded: Boolean) {
|
||||||
_updateFeedDialogState.update {
|
_updateFeedDialogState.update {
|
||||||
@ -311,16 +326,24 @@ class FeedScreenModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
|
_updateFeedDialogState.update { it.copy(exception = null) }
|
||||||
|
|
||||||
screenModelScope.launch(Dispatchers.IO) {
|
screenModelScope.launch(Dispatchers.IO) {
|
||||||
with(_updateFeedDialogState.value) {
|
with(_updateFeedDialogState.value) {
|
||||||
|
try {
|
||||||
repository?.updateFeed(
|
repository?.updateFeed(
|
||||||
Feed(
|
Feed(
|
||||||
id = feedId,
|
id = feedId,
|
||||||
name = feedName,
|
name = feedName,
|
||||||
url = feedUrl,
|
url = feedUrl,
|
||||||
folderId = selectedFolder?.id
|
folderId = selectedFolder?.id,
|
||||||
|
remoteFolderId = selectedFolder?.remoteId
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
_updateFeedDialogState.update { it.copy(exception = e) }
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
closeDialog()
|
closeDialog()
|
||||||
@ -329,9 +352,9 @@ class FeedScreenModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// update feed
|
//endregion
|
||||||
|
|
||||||
// add/update folder
|
//region Add/Update folder
|
||||||
|
|
||||||
fun setFolderName(name: String) = _folderState.update {
|
fun setFolderName(name: String) = _folderState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
@ -369,5 +392,5 @@ class FeedScreenModel(
|
|||||||
|
|
||||||
fun resetException() = _feedState.update { it.copy(exception = null) }
|
fun resetException() = _feedState.update { it.copy(exception = null) }
|
||||||
|
|
||||||
// add/update folder
|
//endregion
|
||||||
}
|
}
|
@ -34,6 +34,7 @@ data class AddFeedDialogState(
|
|||||||
val selectedAccount: Account = Account(accountName = ""),
|
val selectedAccount: Account = Account(accountName = ""),
|
||||||
val accounts: List<Account> = listOf(),
|
val accounts: List<Account> = listOf(),
|
||||||
val error: TextFieldError? = null,
|
val error: TextFieldError? = null,
|
||||||
|
val exception: Exception? = null
|
||||||
) {
|
) {
|
||||||
val isError: Boolean get() = error != null
|
val isError: Boolean get() = error != null
|
||||||
}
|
}
|
||||||
@ -48,7 +49,8 @@ data class UpdateFeedDialogState(
|
|||||||
val folders: List<Folder> = listOf(),
|
val folders: List<Folder> = listOf(),
|
||||||
val isAccountDropDownExpanded: Boolean = false,
|
val isAccountDropDownExpanded: Boolean = false,
|
||||||
val isFeedUrlReadOnly: Boolean = true,
|
val isFeedUrlReadOnly: Boolean = true,
|
||||||
val isNoFolderCase: Boolean = false
|
val isNoFolderCase: Boolean = false,
|
||||||
|
val exception: Exception? = null
|
||||||
) {
|
) {
|
||||||
val isFeedNameError
|
val isFeedNameError
|
||||||
get() = feedNameError != null
|
get() = feedNameError != null
|
||||||
|
@ -123,7 +123,7 @@ object FeedTab : Tab {
|
|||||||
is DialogState.UpdateFeed -> {
|
is DialogState.UpdateFeed -> {
|
||||||
UpdateFeedDialog(
|
UpdateFeedDialog(
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
onDismissRequest = { viewModel.closeDialog() }
|
onDismissRequest = { viewModel.closeDialog(dialog) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import androidx.compose.material3.ExposedDropdownMenuBox
|
|||||||
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
@ -19,15 +20,19 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.readrops.app.compose.R
|
import com.readrops.app.compose.R
|
||||||
import com.readrops.app.compose.account.selection.adaptiveIconPainterResource
|
import com.readrops.app.compose.account.selection.adaptiveIconPainterResource
|
||||||
import com.readrops.app.compose.feeds.FeedScreenModel
|
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.components.BaseDialog
|
||||||
import com.readrops.app.compose.util.theme.LargeSpacer
|
import com.readrops.app.compose.util.theme.LargeSpacer
|
||||||
|
import com.readrops.app.compose.util.theme.MediumSpacer
|
||||||
import com.readrops.app.compose.util.theme.ShortSpacer
|
import com.readrops.app.compose.util.theme.ShortSpacer
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@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()
|
LargeSpacer()
|
||||||
|
|
||||||
TextButton(
|
TextButton(
|
||||||
|
@ -5,17 +5,20 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
|||||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||||
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.readrops.app.compose.R
|
import com.readrops.app.compose.R
|
||||||
import com.readrops.app.compose.feeds.FeedScreenModel
|
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.components.BaseDialog
|
||||||
import com.readrops.app.compose.util.theme.LargeSpacer
|
import com.readrops.app.compose.util.theme.LargeSpacer
|
||||||
import com.readrops.app.compose.util.theme.MediumSpacer
|
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()
|
LargeSpacer()
|
||||||
|
|
||||||
TextButton(
|
TextButton(
|
||||||
|
@ -85,7 +85,31 @@ class FreshRSSRepository(
|
|||||||
override suspend fun insertNewFeeds(
|
override suspend fun insertNewFeeds(
|
||||||
newFeeds: List<Feed>,
|
newFeeds: List<Feed>,
|
||||||
onUpdate: (Feed) -> Unit
|
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) {
|
override suspend fun updateFolder(folder: Folder) {
|
||||||
dataSource.updateFolder(account.writeToken!!, folder.remoteId!!, folder.name!!)
|
dataSource.updateFolder(account.writeToken!!, folder.remoteId!!, folder.name!!)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user