Add AddFolderDialog in FeedTab

This commit is contained in:
Shinokuni 2024-01-19 21:35:55 +01:00
parent caf55451d3
commit 2631712361
8 changed files with 244 additions and 22 deletions

View File

@ -87,3 +87,9 @@ data class UpdateFeedDialogState(
get() = accountType != null && !accountType.accountConfig!!.isFeedUrlEditable
}
data class AddFolderState(
val name: String = "",
val isEmpty: Boolean = false,
val errorText: String = "Field can't be empty"
)

View File

@ -1,8 +1,10 @@
package com.readrops.app.compose.feeds
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
@ -12,12 +14,12 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
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 com.readrops.app.compose.R
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.FeedModalBottomSheet
import com.readrops.app.compose.feeds.dialogs.UpdateFeedDialog
import com.readrops.app.compose.util.components.Placeholder
import com.readrops.app.compose.util.theme.spacing
import com.readrops.db.entities.Feed
import org.koin.androidx.compose.getViewModel
@ -85,7 +89,14 @@ object FeedTab : Tab {
uriHandler.openUri(dialog.feed.siteUrl!!)
viewModel.closeDialog()
},
onUpdate = { viewModel.openDialog(DialogState.UpdateFeed(dialog.feed, dialog.folder)) },
onUpdate = {
viewModel.openDialog(
DialogState.UpdateFeed(
dialog.feed,
dialog.folder
)
)
},
onUpdateColor = {},
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.UpdateFolder -> {}
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 ->
Box(
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
)
}
}
}
}

View File

@ -35,6 +35,9 @@ class FeedViewModel(
private val _updateFeedDialogState = MutableStateFlow(UpdateFeedDialogState())
val updateFeedDialogState = _updateFeedDialogState.asStateFlow()
private val _addFolderState = MutableStateFlow(AddFolderState())
val addFolderState = _addFolderState.asStateFlow()
init {
viewModelScope.launch(context = Dispatchers.IO) {
accountEvent
@ -245,10 +248,47 @@ class FeedViewModel(
}
}
}
}
// 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
}

View File

@ -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)
}
}
)
}
}

View File

@ -47,13 +47,13 @@ abstract class BaseRepository(
account: 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) {
database.newItemDao().updateReadState(item.id, item.isRead)

View File

@ -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")
}
}
}
}
}

View 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>

View File

@ -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")
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)
}