mirror of
https://github.com/readrops/Readrops.git
synced 2025-02-09 16:38:40 +01:00
Add AddFolderDialog in FeedTab
This commit is contained in:
parent
caf55451d3
commit
2631712361
@ -87,3 +87,9 @@ data class UpdateFeedDialogState(
|
|||||||
get() = accountType != null && !accountType.accountConfig!!.isFeedUrlEditable
|
get() = accountType != null && !accountType.accountConfig!!.isFeedUrlEditable
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class AddFolderState(
|
||||||
|
val name: String = "",
|
||||||
|
val isEmpty: Boolean = false,
|
||||||
|
val errorText: String = "Field can't be empty"
|
||||||
|
)
|
@ -1,8 +1,10 @@
|
|||||||
package com.readrops.app.compose.feeds
|
package com.readrops.app.compose.feeds
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@ -12,12 +14,12 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
|||||||
import androidx.compose.material3.FloatingActionButton
|
import androidx.compose.material3.FloatingActionButton
|
||||||
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.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
@ -29,10 +31,12 @@ import cafe.adriel.voyager.navigator.tab.Tab
|
|||||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||||
import com.readrops.app.compose.R
|
import com.readrops.app.compose.R
|
||||||
import com.readrops.app.compose.feeds.dialogs.AddFeedDialog
|
import com.readrops.app.compose.feeds.dialogs.AddFeedDialog
|
||||||
|
import com.readrops.app.compose.feeds.dialogs.AddFolderDialog
|
||||||
import com.readrops.app.compose.feeds.dialogs.DeleteFeedDialog
|
import com.readrops.app.compose.feeds.dialogs.DeleteFeedDialog
|
||||||
import com.readrops.app.compose.feeds.dialogs.FeedModalBottomSheet
|
import com.readrops.app.compose.feeds.dialogs.FeedModalBottomSheet
|
||||||
import com.readrops.app.compose.feeds.dialogs.UpdateFeedDialog
|
import com.readrops.app.compose.feeds.dialogs.UpdateFeedDialog
|
||||||
import com.readrops.app.compose.util.components.Placeholder
|
import com.readrops.app.compose.util.components.Placeholder
|
||||||
|
import com.readrops.app.compose.util.theme.spacing
|
||||||
import com.readrops.db.entities.Feed
|
import com.readrops.db.entities.Feed
|
||||||
import org.koin.androidx.compose.getViewModel
|
import org.koin.androidx.compose.getViewModel
|
||||||
|
|
||||||
@ -85,7 +89,14 @@ object FeedTab : Tab {
|
|||||||
uriHandler.openUri(dialog.feed.siteUrl!!)
|
uriHandler.openUri(dialog.feed.siteUrl!!)
|
||||||
viewModel.closeDialog()
|
viewModel.closeDialog()
|
||||||
},
|
},
|
||||||
onUpdate = { viewModel.openDialog(DialogState.UpdateFeed(dialog.feed, dialog.folder)) },
|
onUpdate = {
|
||||||
|
viewModel.openDialog(
|
||||||
|
DialogState.UpdateFeed(
|
||||||
|
dialog.feed,
|
||||||
|
dialog.folder
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
onUpdateColor = {},
|
onUpdateColor = {},
|
||||||
onDelete = { viewModel.openDialog(DialogState.DeleteFeed(dialog.feed)) },
|
onDelete = { viewModel.openDialog(DialogState.DeleteFeed(dialog.feed)) },
|
||||||
)
|
)
|
||||||
@ -98,7 +109,17 @@ object FeedTab : Tab {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
DialogState.AddFolder -> {}
|
DialogState.AddFolder -> {
|
||||||
|
AddFolderDialog(
|
||||||
|
viewModel = viewModel,
|
||||||
|
onDismiss = {
|
||||||
|
viewModel.closeDialog()
|
||||||
|
viewModel.resetAddFolderState()
|
||||||
|
}
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
is DialogState.DeleteFolder -> {}
|
is DialogState.DeleteFolder -> {}
|
||||||
is DialogState.UpdateFolder -> {}
|
is DialogState.UpdateFolder -> {}
|
||||||
null -> {}
|
null -> {}
|
||||||
@ -122,6 +143,34 @@ object FeedTab : Tab {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
floatingActionButton = {
|
||||||
|
Column {
|
||||||
|
FloatingActionButton(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(
|
||||||
|
end = 0.dp,
|
||||||
|
bottom = MaterialTheme.spacing.mediumSpacing
|
||||||
|
)
|
||||||
|
.size(40.dp),
|
||||||
|
onClick = { viewModel.openDialog(DialogState.AddFolder) }
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = R.drawable.ic_new_folder),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = { viewModel.openDialog(DialogState.AddFeed) }
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Add,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -191,18 +240,6 @@ object FeedTab : Tab {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FloatingActionButton(
|
|
||||||
modifier = Modifier
|
|
||||||
.align(Alignment.BottomEnd)
|
|
||||||
.padding(16.dp),
|
|
||||||
onClick = { viewModel.openDialog(DialogState.AddFeed) }
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Add,
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,9 @@ class FeedViewModel(
|
|||||||
private val _updateFeedDialogState = MutableStateFlow(UpdateFeedDialogState())
|
private val _updateFeedDialogState = MutableStateFlow(UpdateFeedDialogState())
|
||||||
val updateFeedDialogState = _updateFeedDialogState.asStateFlow()
|
val updateFeedDialogState = _updateFeedDialogState.asStateFlow()
|
||||||
|
|
||||||
|
private val _addFolderState = MutableStateFlow(AddFolderState())
|
||||||
|
val addFolderState = _addFolderState.asStateFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch(context = Dispatchers.IO) {
|
viewModelScope.launch(context = Dispatchers.IO) {
|
||||||
accountEvent
|
accountEvent
|
||||||
@ -245,10 +248,47 @@ class FeedViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// update feed
|
// update feed
|
||||||
|
|
||||||
|
// add folder
|
||||||
|
|
||||||
|
fun setFolderName(name: String) = _addFolderState.update {
|
||||||
|
it.copy(
|
||||||
|
name = name,
|
||||||
|
isEmpty = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addFolderValidate() {
|
||||||
|
val name = _addFolderState.value.name
|
||||||
|
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
_addFolderState.update {
|
||||||
|
it.copy(isEmpty = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
repository?.addFolder(Folder(name = name, accountId = currentAccount?.id!!))
|
||||||
|
|
||||||
|
closeDialog()
|
||||||
|
resetAddFolderState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetAddFolderState() {
|
||||||
|
_addFolderState.update {
|
||||||
|
it.copy(
|
||||||
|
name = "",
|
||||||
|
isEmpty = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add folder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.readrops.app.compose.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.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import com.readrops.app.compose.R
|
||||||
|
import com.readrops.app.compose.feeds.FeedViewModel
|
||||||
|
import com.readrops.app.compose.util.components.BaseDialog
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AddFolderDialog(
|
||||||
|
viewModel: FeedViewModel,
|
||||||
|
onDismiss: () -> Unit
|
||||||
|
) {
|
||||||
|
val state by viewModel.addFolderState.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
|
BaseDialog(
|
||||||
|
title = "Add Folder",
|
||||||
|
icon = painterResource(id = R.drawable.ic_new_folder),
|
||||||
|
onDismiss = { onDismiss() },
|
||||||
|
onValidate = { viewModel.addFolderValidate() }
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = state.name,
|
||||||
|
label = {
|
||||||
|
Text(text = "URL")
|
||||||
|
},
|
||||||
|
onValueChange = { viewModel.setFolderName(it) },
|
||||||
|
singleLine = true,
|
||||||
|
trailingIcon = {
|
||||||
|
if (state.name.isNotEmpty()) {
|
||||||
|
IconButton(
|
||||||
|
onClick = { viewModel.setFolderName("") }
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Clear,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isError = state.isEmpty,
|
||||||
|
supportingText = {
|
||||||
|
if (state.isEmpty) {
|
||||||
|
Text(text = state.errorText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -47,13 +47,13 @@ abstract class BaseRepository(
|
|||||||
account: Account,
|
account: Account,
|
||||||
) : ARepository(database, account) {
|
) : ARepository(database, account) {
|
||||||
|
|
||||||
open suspend fun updateFeed(feed: Feed) {}
|
open suspend fun updateFeed(feed: Feed) = database.newFeedDao().updateFeedFields(feed.id, feed.name!!, feed.url!!, feed.folderId!!)
|
||||||
|
|
||||||
open suspend fun deleteFeed(feed: Feed) {}
|
open suspend fun deleteFeed(feed: Feed) = database.newFeedDao().delete(feed)
|
||||||
|
|
||||||
open suspend fun addFolder(folder: Folder) {}
|
open suspend fun addFolder(folder: Folder) = database.newFolderDao().insert(folder)
|
||||||
|
|
||||||
open suspend fun deleteFolder(folder: Folder) {}
|
open suspend fun deleteFolder(folder: Folder) = database.newFolderDao().delete(folder)
|
||||||
|
|
||||||
open suspend fun setItemReadState(item: Item) {
|
open suspend fun setItemReadState(item: Item) {
|
||||||
database.newItemDao().updateReadState(item.id, item.isRead)
|
database.newItemDao().updateReadState(item.id, item.isRead)
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
package com.readrops.app.compose.util.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import com.readrops.app.compose.util.theme.LargeSpacer
|
||||||
|
import com.readrops.app.compose.util.theme.MediumSpacer
|
||||||
|
import com.readrops.app.compose.util.theme.spacing
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BaseDialog(
|
||||||
|
title: String,
|
||||||
|
icon: Painter,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onValidate: () -> Unit,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = onDismiss
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
shape = RoundedCornerShape(16.dp)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(MaterialTheme.colorScheme.background)
|
||||||
|
.padding(MaterialTheme.spacing.largeSpacing)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(MaterialTheme.spacing.largeSpacing)
|
||||||
|
)
|
||||||
|
|
||||||
|
MediumSpacer()
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.headlineSmall
|
||||||
|
)
|
||||||
|
|
||||||
|
MediumSpacer()
|
||||||
|
|
||||||
|
content()
|
||||||
|
|
||||||
|
LargeSpacer()
|
||||||
|
|
||||||
|
TextButton(
|
||||||
|
onClick = { onValidate() },
|
||||||
|
) {
|
||||||
|
Text(text = "Validate")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
appcompose/src/main/res/drawable/ic_new_folder.xml
Normal file
5
appcompose/src/main/res/drawable/ic_new_folder.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M20,6h-8l-2,-2L4,4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM19,14h-3v3h-2v-3h-3v-2h3L14,9h2v3h3v2z"/>
|
||||||
|
</vector>
|
@ -25,4 +25,7 @@ abstract class NewFeedDao : NewBaseDao<Feed> {
|
|||||||
"Where Feed.folder_id is Null And Item.read = 0 And Feed.account_id = :accountId Group by Feed.id")
|
"Where Feed.folder_id is Null And Item.read = 0 And Feed.account_id = :accountId Group by Feed.id")
|
||||||
abstract fun selectFeedsWithoutFolder(accountId: Int): Flow<List<FeedWithCount>>
|
abstract fun selectFeedsWithoutFolder(accountId: Int): Flow<List<FeedWithCount>>
|
||||||
|
|
||||||
|
@Query("Update Feed set name = :feedName, url = :feedUrl, folder_id = :folderId Where id = :feedId")
|
||||||
|
abstract fun updateFeedFields(feedId: Int, feedName: String, feedUrl: String, folderId: Int)
|
||||||
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user