Replace FolderDialog by TextFieldDialog in FeedTab
This commit is contained in:
parent
feeed2771f
commit
8e8d1ce9c8
@ -9,11 +9,13 @@ import com.readrops.app.R
|
||||
import com.readrops.app.base.TabScreenModel
|
||||
import com.readrops.app.repositories.GetFoldersWithFeeds
|
||||
import com.readrops.app.util.components.TextFieldError
|
||||
import com.readrops.app.util.components.dialog.TextFieldDialogState
|
||||
import com.readrops.db.Database
|
||||
import com.readrops.db.entities.Feed
|
||||
import com.readrops.db.entities.Folder
|
||||
import com.readrops.db.entities.account.Account
|
||||
import com.readrops.db.filters.MainFilter
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@ -31,7 +33,8 @@ class FeedScreenModel(
|
||||
database: Database,
|
||||
private val getFoldersWithFeeds: GetFoldersWithFeeds,
|
||||
private val localRSSDataSource: LocalRSSDataSource,
|
||||
private val context: Context
|
||||
private val context: Context,
|
||||
private val dispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||
) : TabScreenModel(database), KoinComponent {
|
||||
|
||||
private val _feedState = MutableStateFlow(FeedState())
|
||||
@ -43,11 +46,11 @@ class FeedScreenModel(
|
||||
private val _updateFeedDialogState = MutableStateFlow(UpdateFeedDialogState())
|
||||
val updateFeedDialogState = _updateFeedDialogState.asStateFlow()
|
||||
|
||||
private val _folderState = MutableStateFlow(FolderState())
|
||||
private val _folderState = MutableStateFlow(TextFieldDialogState())
|
||||
val folderState = _folderState.asStateFlow()
|
||||
|
||||
init {
|
||||
screenModelScope.launch(context = Dispatchers.IO) {
|
||||
screenModelScope.launch(dispatcher) {
|
||||
accountEvent
|
||||
.flatMapLatest { account ->
|
||||
_feedState.update { it.copy(displayFolderCreationButton = account.config.canCreateFolder) }
|
||||
@ -57,7 +60,11 @@ class FeedScreenModel(
|
||||
)
|
||||
}
|
||||
|
||||
getFoldersWithFeeds.get(account.id, MainFilter.ALL, account.config.useSeparateState)
|
||||
getFoldersWithFeeds.get(
|
||||
account.id,
|
||||
MainFilter.ALL,
|
||||
account.config.useSeparateState
|
||||
)
|
||||
}
|
||||
.catch { throwable ->
|
||||
_feedState.update {
|
||||
@ -72,7 +79,7 @@ class FeedScreenModel(
|
||||
|
||||
}
|
||||
|
||||
screenModelScope.launch(context = Dispatchers.IO) {
|
||||
screenModelScope.launch(dispatcher) {
|
||||
database.accountDao()
|
||||
.selectAllAccounts()
|
||||
.collect { accounts ->
|
||||
@ -87,17 +94,16 @@ class FeedScreenModel(
|
||||
}
|
||||
}
|
||||
|
||||
screenModelScope.launch(context = Dispatchers.IO) {
|
||||
screenModelScope.launch(dispatcher) {
|
||||
accountEvent.flatMapLatest { account ->
|
||||
_updateFeedDialogState.update {
|
||||
it.copy(
|
||||
isFeedUrlReadOnly = account.config.isFeedUrlReadOnly,
|
||||
)
|
||||
}
|
||||
|
||||
database.folderDao()
|
||||
.selectFolders(account.id)
|
||||
_updateFeedDialogState.update {
|
||||
it.copy(
|
||||
isFeedUrlReadOnly = account.config.isFeedUrlReadOnly,
|
||||
)
|
||||
}
|
||||
|
||||
database.folderDao().selectFolders(account.id)
|
||||
}
|
||||
.collect { folders ->
|
||||
_updateFeedDialogState.update {
|
||||
it.copy(
|
||||
@ -131,18 +137,21 @@ class FeedScreenModel(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is DialogState.AddFolder, is DialogState.UpdateFolder -> {
|
||||
_folderState.update {
|
||||
it.copy(
|
||||
folder = Folder(),
|
||||
nameError = null,
|
||||
value = "",
|
||||
textFieldError = null,
|
||||
exception = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is DialogState.UpdateFeed -> {
|
||||
_updateFeedDialogState.update { it.copy(exception = null) }
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
|
||||
@ -165,7 +174,7 @@ class FeedScreenModel(
|
||||
if (state is DialogState.UpdateFolder) {
|
||||
_folderState.update {
|
||||
it.copy(
|
||||
folder = state.folder
|
||||
value = state.folder.name.orEmpty()
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -174,7 +183,7 @@ class FeedScreenModel(
|
||||
}
|
||||
|
||||
fun deleteFeed(feed: Feed) {
|
||||
screenModelScope.launch(Dispatchers.IO) {
|
||||
screenModelScope.launch(dispatcher) {
|
||||
try {
|
||||
repository?.deleteFeed(feed)
|
||||
} catch (e: Exception) {
|
||||
@ -184,7 +193,7 @@ class FeedScreenModel(
|
||||
}
|
||||
|
||||
fun deleteFolder(folder: Folder) {
|
||||
screenModelScope.launch(Dispatchers.IO) {
|
||||
screenModelScope.launch(dispatcher) {
|
||||
try {
|
||||
repository?.deleteFolder(folder)
|
||||
} catch (e: Exception) {
|
||||
@ -228,7 +237,7 @@ class FeedScreenModel(
|
||||
return
|
||||
}
|
||||
|
||||
else -> screenModelScope.launch(Dispatchers.IO) {
|
||||
else -> screenModelScope.launch(dispatcher) {
|
||||
try {
|
||||
if (localRSSDataSource.isUrlRSSResource(url)) {
|
||||
insertFeeds(listOf(Feed(url = url)))
|
||||
@ -329,7 +338,7 @@ class FeedScreenModel(
|
||||
else -> {
|
||||
_updateFeedDialogState.update { it.copy(exception = null) }
|
||||
|
||||
screenModelScope.launch(Dispatchers.IO) {
|
||||
screenModelScope.launch(dispatcher) {
|
||||
with(_updateFeedDialogState.value) {
|
||||
try {
|
||||
repository?.updateFeed(
|
||||
@ -362,28 +371,26 @@ class FeedScreenModel(
|
||||
|
||||
fun setFolderName(name: String) = _folderState.update {
|
||||
it.copy(
|
||||
folder = it.folder.copy(name = name),
|
||||
nameError = null,
|
||||
value = name,
|
||||
textFieldError = null,
|
||||
)
|
||||
}
|
||||
|
||||
fun folderValidate(updateFolder: Boolean = false) {
|
||||
val name = _folderState.value.name.orEmpty()
|
||||
val name = _folderState.value.value
|
||||
|
||||
if (name.isEmpty()) {
|
||||
_folderState.update { it.copy(nameError = TextFieldError.EmptyField) }
|
||||
|
||||
_folderState.update { it.copy(textFieldError = TextFieldError.EmptyField) }
|
||||
return
|
||||
}
|
||||
|
||||
screenModelScope.launch(Dispatchers.IO) {
|
||||
screenModelScope.launch(dispatcher) {
|
||||
try {
|
||||
if (updateFolder) {
|
||||
repository?.updateFolder(_folderState.value.folder)
|
||||
val folder = (_feedState.value.dialog as DialogState.UpdateFolder).folder
|
||||
repository?.updateFolder(folder.copy(name = name))
|
||||
} else {
|
||||
repository?.addFolder(_folderState.value.folder.apply {
|
||||
accountId = currentAccount!!.id
|
||||
})
|
||||
repository?.addFolder(Folder(name = name, accountId = currentAccount!!.id))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
_folderState.update { it.copy(exception = e) }
|
||||
|
@ -44,12 +44,12 @@ import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||
import com.readrops.app.R
|
||||
import com.readrops.app.feeds.dialogs.AddFeedDialog
|
||||
import com.readrops.app.feeds.dialogs.FeedModalBottomSheet
|
||||
import com.readrops.app.feeds.dialogs.FolderDialog
|
||||
import com.readrops.app.feeds.dialogs.UpdateFeedDialog
|
||||
import com.readrops.app.util.ErrorMessage
|
||||
import com.readrops.app.util.components.CenteredProgressIndicator
|
||||
import com.readrops.app.util.components.ErrorMessage
|
||||
import com.readrops.app.util.components.Placeholder
|
||||
import com.readrops.app.util.components.dialog.TextFieldDialog
|
||||
import com.readrops.app.util.components.dialog.TwoChoicesDialog
|
||||
import com.readrops.app.util.theme.spacing
|
||||
import com.readrops.db.entities.Feed
|
||||
@ -70,109 +70,24 @@ object FeedTab : Tab {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val context = LocalContext.current
|
||||
|
||||
val viewModel = getScreenModel<FeedScreenModel>()
|
||||
val state by viewModel.feedsState.collectAsStateWithLifecycle()
|
||||
val screenModel = getScreenModel<FeedScreenModel>()
|
||||
val state by screenModel.feedsState.collectAsStateWithLifecycle()
|
||||
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val topAppBarScrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
val topAppBarScrollBehavior =
|
||||
TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
|
||||
LaunchedEffect(state.exception) {
|
||||
if (state.exception != null) {
|
||||
snackbarHostState.showSnackbar(ErrorMessage.get(state.exception!!, context))
|
||||
viewModel.resetException()
|
||||
screenModel.resetException()
|
||||
}
|
||||
}
|
||||
|
||||
when (val dialog = state.dialog) {
|
||||
is DialogState.AddFeed -> {
|
||||
AddFeedDialog(
|
||||
viewModel = viewModel,
|
||||
onDismiss = {
|
||||
viewModel.closeDialog(DialogState.AddFeed)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
is DialogState.DeleteFeed -> {
|
||||
TwoChoicesDialog(
|
||||
title = stringResource(R.string.delete_feed),
|
||||
text = "Do you want to delete feed ${dialog.feed.name}?",
|
||||
icon = rememberVectorPainter(image = Icons.Default.Delete),
|
||||
confirmText = stringResource(R.string.delete),
|
||||
dismissText = stringResource(R.string.cancel),
|
||||
onDismiss = { viewModel.closeDialog() },
|
||||
onConfirm = {
|
||||
viewModel.deleteFeed(dialog.feed)
|
||||
viewModel.closeDialog()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
is DialogState.FeedSheet -> {
|
||||
FeedModalBottomSheet(
|
||||
feed = dialog.feed,
|
||||
onDismissRequest = { viewModel.closeDialog() },
|
||||
onOpen = {
|
||||
uriHandler.openUri(dialog.feed.siteUrl!!)
|
||||
viewModel.closeDialog()
|
||||
},
|
||||
onUpdate = {
|
||||
viewModel.openDialog(DialogState.UpdateFeed(dialog.feed, dialog.folder))
|
||||
},
|
||||
onUpdateColor = {},
|
||||
onDelete = { viewModel.openDialog(DialogState.DeleteFeed(dialog.feed)) }
|
||||
)
|
||||
}
|
||||
|
||||
is DialogState.UpdateFeed -> {
|
||||
UpdateFeedDialog(
|
||||
viewModel = viewModel,
|
||||
onDismissRequest = { viewModel.closeDialog(dialog) }
|
||||
)
|
||||
}
|
||||
|
||||
DialogState.AddFolder -> {
|
||||
FolderDialog(
|
||||
viewModel = viewModel,
|
||||
onDismiss = {
|
||||
viewModel.closeDialog(DialogState.AddFolder)
|
||||
},
|
||||
onValidate = {
|
||||
viewModel.folderValidate()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
is DialogState.DeleteFolder -> {
|
||||
TwoChoicesDialog(
|
||||
title = stringResource(R.string.delete_folder),
|
||||
text = "Do you want to delete folder ${dialog.folder.name}?",
|
||||
icon = rememberVectorPainter(image = Icons.Default.Delete),
|
||||
confirmText = stringResource(R.string.delete),
|
||||
dismissText = stringResource(R.string.cancel),
|
||||
onDismiss = { viewModel.closeDialog() },
|
||||
onConfirm = {
|
||||
viewModel.deleteFolder(dialog.folder)
|
||||
viewModel.closeDialog()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
is DialogState.UpdateFolder -> {
|
||||
FolderDialog(
|
||||
updateFolder = true,
|
||||
viewModel = viewModel,
|
||||
onDismiss = {
|
||||
viewModel.closeDialog(DialogState.UpdateFolder(dialog.folder))
|
||||
},
|
||||
onValidate = {
|
||||
viewModel.folderValidate(updateFolder = true)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
null -> {}
|
||||
}
|
||||
FeedDialogs(
|
||||
state = state,
|
||||
screenModel = screenModel
|
||||
)
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
@ -180,7 +95,7 @@ object FeedTab : Tab {
|
||||
title = { Text(text = stringResource(R.string.feeds)) },
|
||||
actions = {
|
||||
IconButton(
|
||||
onClick = { viewModel.setFolderExpandState(state.areFoldersExpanded.not()) }
|
||||
onClick = { screenModel.setFolderExpandState(state.areFoldersExpanded.not()) }
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(
|
||||
@ -205,7 +120,7 @@ object FeedTab : Tab {
|
||||
start = MaterialTheme.spacing.veryShortSpacing,
|
||||
bottom = MaterialTheme.spacing.shortSpacing
|
||||
),
|
||||
onClick = { viewModel.openDialog(DialogState.AddFolder) }
|
||||
onClick = { screenModel.openDialog(DialogState.AddFolder) }
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_new_folder),
|
||||
@ -216,7 +131,7 @@ object FeedTab : Tab {
|
||||
}
|
||||
|
||||
FloatingActionButton(
|
||||
onClick = { viewModel.openDialog(DialogState.AddFeed) }
|
||||
onClick = { screenModel.openDialog(DialogState.AddFeed) }
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Add,
|
||||
@ -256,18 +171,18 @@ object FeedTab : Tab {
|
||||
feeds = folderWithFeeds.second,
|
||||
isExpanded = state.areFoldersExpanded,
|
||||
onFeedClick = { feed ->
|
||||
viewModel.openDialog(
|
||||
screenModel.openDialog(
|
||||
DialogState.FeedSheet(feed, folder)
|
||||
)
|
||||
},
|
||||
onFeedLongClick = { feed -> onFeedLongClick(feed) },
|
||||
onUpdateFolder = {
|
||||
viewModel.openDialog(
|
||||
screenModel.openDialog(
|
||||
DialogState.UpdateFolder(folder)
|
||||
)
|
||||
},
|
||||
onDeleteFolder = {
|
||||
viewModel.openDialog(
|
||||
screenModel.openDialog(
|
||||
DialogState.DeleteFolder(folder)
|
||||
)
|
||||
}
|
||||
@ -279,7 +194,7 @@ object FeedTab : Tab {
|
||||
FeedItem(
|
||||
feed = feed,
|
||||
onClick = {
|
||||
viewModel.openDialog(
|
||||
screenModel.openDialog(
|
||||
DialogState.FeedSheet(feed, null)
|
||||
)
|
||||
},
|
||||
@ -310,4 +225,102 @@ object FeedTab : Tab {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FeedDialogs(state: FeedState, screenModel: FeedScreenModel) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
val folderState by screenModel.folderState.collectAsStateWithLifecycle()
|
||||
|
||||
when (val dialog = state.dialog) {
|
||||
is DialogState.AddFeed -> {
|
||||
AddFeedDialog(
|
||||
viewModel = screenModel,
|
||||
onDismiss = {
|
||||
screenModel.closeDialog(DialogState.AddFeed)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
is DialogState.DeleteFeed -> {
|
||||
TwoChoicesDialog(
|
||||
title = stringResource(R.string.delete_feed),
|
||||
text = "Do you want to delete feed ${dialog.feed.name}?",
|
||||
icon = rememberVectorPainter(image = Icons.Default.Delete),
|
||||
confirmText = stringResource(R.string.delete),
|
||||
dismissText = stringResource(R.string.cancel),
|
||||
onDismiss = { screenModel.closeDialog() },
|
||||
onConfirm = {
|
||||
screenModel.deleteFeed(dialog.feed)
|
||||
screenModel.closeDialog()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
is DialogState.FeedSheet -> {
|
||||
FeedModalBottomSheet(
|
||||
feed = dialog.feed,
|
||||
onDismissRequest = { screenModel.closeDialog() },
|
||||
onOpen = {
|
||||
uriHandler.openUri(dialog.feed.siteUrl!!)
|
||||
screenModel.closeDialog()
|
||||
},
|
||||
onUpdate = {
|
||||
screenModel.openDialog(DialogState.UpdateFeed(dialog.feed, dialog.folder))
|
||||
},
|
||||
onUpdateColor = {},
|
||||
onDelete = { screenModel.openDialog(DialogState.DeleteFeed(dialog.feed)) }
|
||||
)
|
||||
}
|
||||
|
||||
is DialogState.UpdateFeed -> {
|
||||
UpdateFeedDialog(
|
||||
viewModel = screenModel,
|
||||
onDismissRequest = { screenModel.closeDialog(dialog) }
|
||||
)
|
||||
}
|
||||
|
||||
DialogState.AddFolder -> {
|
||||
TextFieldDialog(
|
||||
title = stringResource(id = R.string.add_folder),
|
||||
icon = painterResource(id = R.drawable.ic_new_folder),
|
||||
label = stringResource(id = R.string.name),
|
||||
state = folderState,
|
||||
onValueChange = { screenModel.setFolderName(it) },
|
||||
onValidate = { screenModel.folderValidate() },
|
||||
onDismiss = { screenModel.closeDialog(DialogState.AddFolder) }
|
||||
)
|
||||
}
|
||||
|
||||
is DialogState.DeleteFolder -> {
|
||||
TwoChoicesDialog(
|
||||
title = stringResource(R.string.delete_folder),
|
||||
text = "Do you want to delete folder ${dialog.folder.name}?",
|
||||
icon = rememberVectorPainter(image = Icons.Default.Delete),
|
||||
confirmText = stringResource(R.string.delete),
|
||||
dismissText = stringResource(R.string.cancel),
|
||||
onDismiss = { screenModel.closeDialog() },
|
||||
onConfirm = {
|
||||
screenModel.deleteFolder(dialog.folder)
|
||||
screenModel.closeDialog()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
is DialogState.UpdateFolder -> {
|
||||
TextFieldDialog(
|
||||
title = stringResource(id = R.string.edit_folder),
|
||||
icon = painterResource(id = R.drawable.ic_folder_grey),
|
||||
label = stringResource(id = R.string.name),
|
||||
state = folderState,
|
||||
onValueChange = { screenModel.setFolderName(it) },
|
||||
onValidate = { screenModel.folderValidate(updateFolder = true) },
|
||||
onDismiss = { screenModel.closeDialog(DialogState.UpdateFolder(dialog.folder)) }
|
||||
)
|
||||
}
|
||||
|
||||
null -> {}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
package com.readrops.app.feeds.dialogs
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Clear
|
||||
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
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
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.R
|
||||
import com.readrops.app.feeds.FeedScreenModel
|
||||
import com.readrops.app.util.ErrorMessage
|
||||
import com.readrops.app.util.components.dialog.BaseDialog
|
||||
import com.readrops.app.util.theme.LargeSpacer
|
||||
|
||||
@Composable
|
||||
fun FolderDialog(
|
||||
updateFolder: Boolean = false,
|
||||
viewModel: FeedScreenModel,
|
||||
onDismiss: () -> Unit,
|
||||
onValidate: () -> Unit
|
||||
) {
|
||||
val state by viewModel.folderState.collectAsStateWithLifecycle()
|
||||
|
||||
BaseDialog(
|
||||
title = stringResource(id = if (updateFolder) R.string.edit_folder else R.string.add_folder),
|
||||
icon = painterResource(id = if (updateFolder) R.drawable.ic_folder_grey else R.drawable.ic_new_folder),
|
||||
onDismiss = onDismiss
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = state.name.orEmpty(),
|
||||
label = {
|
||||
Text(text = "URL")
|
||||
},
|
||||
onValueChange = { viewModel.setFolderName(it) },
|
||||
singleLine = true,
|
||||
trailingIcon = {
|
||||
if (!state.name.isNullOrEmpty()) {
|
||||
IconButton(
|
||||
onClick = { viewModel.setFolderName("") }
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Clear,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
isError = state.isTextFieldError,
|
||||
supportingText = { Text(text = state.nameError?.errorText().orEmpty()) }
|
||||
)
|
||||
|
||||
if (state.exception != null) {
|
||||
Text(
|
||||
text = ErrorMessage.get(state.exception!!, LocalContext.current),
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
}
|
||||
|
||||
LargeSpacer()
|
||||
|
||||
TextButton(
|
||||
onClick = { onValidate() },
|
||||
) {
|
||||
Text(text = stringResource(R.string.validate))
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user