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

View File

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

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, 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)

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