ADD: Delete Collections

This commit is contained in:
sebastian 2025-05-10 15:57:07 +02:00
parent 2525ef9eac
commit 71d56519b2
6 changed files with 411 additions and 150 deletions

View File

@ -46,7 +46,7 @@ import come.stormborntales.notevault.data.repository.CollectionRepository
import come.stormborntales.notevault.data.repository.NoteRepository
import come.stormborntales.notevault.ui.screens.AddNoteDialog
import come.stormborntales.notevault.ui.screens.MainScreen
import come.stormborntales.notevault.ui.screens.NotesScreen
import come.stormborntales.notevault.ui.screens.notebrowser.NotesScreen
import come.stormborntales.notevault.ui.screens.SettingsScreen
import come.stormborntales.notevault.ui.theme.NoteVaultTheme
import come.stormborntales.notevault.ui.viewmodel.NoteBrowserViewModel

View File

@ -0,0 +1,82 @@
package come.stormborntales.notevault.ui.screens.notebrowser
import android.util.Log
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun DeleteCollectionDialog(
collectionName: String,
onDismiss: () -> Unit,
onConfirm: () -> Unit
) {
var sliderPosition by remember { mutableStateOf(0f) }
AlertDialog(
onDismissRequest = onDismiss,
title = {
Text("Collection löschen", style = MaterialTheme.typography.titleLarge)
},
text = {
Column {
Text(
text = "Bist du sicher, dass du die Collection \"$collectionName\" löschen möchtest?",
style = MaterialTheme.typography.bodyMedium
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "⚠️ Alle enthaltenen Noten werden ebenfalls dauerhaft gelöscht.",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Zum Bestätigen den Schieberegler ganz nach rechts ziehen.",
style = MaterialTheme.typography.bodySmall
)
Spacer(modifier = Modifier.height(8.dp))
Slider(
value = sliderPosition,
onValueChange = { sliderPosition = it },
valueRange = 0f..1f,
steps = 0,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 4.dp)
)
}
},
confirmButton = {
TextButton(onClick = {
if(sliderPosition == 1.0f) {
onConfirm()
}
}, colors = ButtonDefaults.textButtonColors(
contentColor = MaterialTheme.colorScheme.error
)) {
Text("Löschen")
}
},
dismissButton = {
TextButton(onClick = onDismiss) {
Text("Abbrechen")
}
}
)
}

View File

@ -0,0 +1,149 @@
package come.stormborntales.notevault.ui.screens.notebrowser
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Folder
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import come.stormborntales.notevault.data.local.entity.NoteCollection
import come.stormborntales.notevault.data.local.entity.NoteEntity
@Composable
fun CollectionItem(
collection: NoteCollection,
onDeleteCollection: (NoteCollection) -> Unit,
) {
var showMenu by remember { mutableStateOf(false) }
Box(
modifier = Modifier
.height(120.dp)
.clip(RoundedCornerShape(12.dp))
.background(MaterialTheme.colorScheme.surfaceVariant)
.clickable { }
.padding(12.dp),
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
imageVector = Icons.Filled.Folder,
contentDescription = "Ordner",
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(64.dp)
)
Box {
val interactionSource = remember { MutableInteractionSource() }
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable { showMenu = true }
.padding(top = 8.dp)
) {
Text(
text = collection.name,
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier
.clickable(
interactionSource = interactionSource,
indication = rememberRipple(bounded = true)
) {
showMenu = true
}
)
Icon(
imageVector = Icons.Default.ArrowDropDown,
contentDescription = "Mehr Optionen",
tint = MaterialTheme.colorScheme.onSurface
)
}
DropdownMenu(
expanded = showMenu,
onDismissRequest = { showMenu = false },
modifier = Modifier
.width(240.dp)
.background(MaterialTheme.colorScheme.background)
) {
DropdownMenuItem(
onClick = {
showMenu = false
},
text = {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = "Bearbeiten",
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(20.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text("Bearbeiten", color = MaterialTheme.colorScheme.primary)
}
}
)
DropdownMenuItem(
onClick = {
showMenu = false
onDeleteCollection(collection)
},
text = {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "Löschen",
tint = MaterialTheme.colorScheme.error,
modifier = Modifier.size(20.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text("Löschen", color = MaterialTheme.colorScheme.error)
}
}
)
}
}
}
}
}

View File

@ -0,0 +1,150 @@
package come.stormborntales.notevault.ui.screens.notebrowser
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import come.stormborntales.notevault.data.local.entity.NoteEntity
@Composable
fun NoteItem(
note: NoteEntity,
onNoteClick: (NoteEntity) -> Unit,
onEditTitle: () -> Unit,
onDeleteNote: () -> Unit
) {
var showMenu by remember { mutableStateOf(false) }
Box(
modifier = Modifier
.height(120.dp)
.clip(RoundedCornerShape(12.dp))
.background(MaterialTheme.colorScheme.surfaceVariant)
.clickable { onNoteClick(note) }
.padding(12.dp),
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
AsyncImage(
model = note.imagePreview,
contentDescription = "Noten-Vorschau",
modifier = Modifier
.size(64.dp)
.clip(RoundedCornerShape(8.dp)),
contentScale = ContentScale.Crop
)
Box {
val interactionSource = remember { MutableInteractionSource() }
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable { showMenu = true }
.padding(top = 8.dp)
) {
Text(
text = note.title,
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier
.clickable(
interactionSource = interactionSource,
indication = rememberRipple(bounded = true)
) {
showMenu = true
}
)
Icon(
imageVector = Icons.Default.ArrowDropDown,
contentDescription = "Mehr Optionen",
tint = MaterialTheme.colorScheme.onSurface
)
}
DropdownMenu(
expanded = showMenu,
onDismissRequest = { showMenu = false },
modifier = Modifier
.width(240.dp)
.background(MaterialTheme.colorScheme.background)
) {
DropdownMenuItem(
onClick = {
showMenu = false
onEditTitle()
},
text = {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = "Bearbeiten",
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(20.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text("Bearbeiten", color = MaterialTheme.colorScheme.primary)
}
}
)
DropdownMenuItem(
onClick = {
showMenu = false
onDeleteNote()
},
text = {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "Löschen",
tint = MaterialTheme.colorScheme.error,
modifier = Modifier.size(20.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text("Löschen", color = MaterialTheme.colorScheme.error)
}
}
)
}
}
}
}
}

View File

@ -1,4 +1,4 @@
package come.stormborntales.notevault.ui.screens
package come.stormborntales.notevault.ui.screens.notebrowser
import android.util.Log
import androidx.activity.compose.BackHandler
@ -19,11 +19,9 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
@ -31,12 +29,12 @@ import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Folder
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
@ -55,18 +53,12 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage
import come.stormborntales.notevault.data.local.AppDatabase
import come.stormborntales.notevault.data.local.entity.NoteCollection
import come.stormborntales.notevault.data.local.entity.NoteEntity
import come.stormborntales.notevault.data.repository.CollectionRepository
import come.stormborntales.notevault.data.repository.NoteRepository
import come.stormborntales.notevault.ui.viewmodel.NoteBrowserViewModel
import come.stormborntales.notevault.ui.viewmodel.NoteBrowserViewModelFactory
import come.stormborntales.notevault.ui.viewmodel.NoteViewModelFactory
@Composable
fun CreateCollectionDialog(
@ -163,117 +155,6 @@ private fun BreadcrumbItem(label: String, onClick: () -> Unit) {
}
}
@Composable
fun NoteItem(
note: NoteEntity,
onNoteClick: (NoteEntity) -> Unit,
onEditTitle: () -> Unit,
onDeleteNote: () -> Unit
) {
var showMenu by remember { mutableStateOf(false) }
Box(
modifier = Modifier
.height(120.dp)
.clip(RoundedCornerShape(12.dp))
.background(MaterialTheme.colorScheme.surfaceVariant)
.clickable { onNoteClick(note) }
.padding(12.dp),
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
AsyncImage(
model = note.imagePreview,
contentDescription = "Noten-Vorschau",
modifier = Modifier
.size(64.dp)
.clip(RoundedCornerShape(8.dp)),
contentScale = ContentScale.Crop
)
Box {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable { showMenu = true }
.padding(top = 8.dp)
) {
Text(
text = note.title,
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
showMenu = true
}
)
Icon(
imageVector = Icons.Default.ArrowDropDown,
contentDescription = "Mehr Optionen",
tint = MaterialTheme.colorScheme.onSurface
)
}
DropdownMenu(
expanded = showMenu,
onDismissRequest = { showMenu = false },
modifier = Modifier
.width(240.dp)
.background(MaterialTheme.colorScheme.background)
) {
DropdownMenuItem(
onClick = {
showMenu = false
onEditTitle()
},
text = {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = "Bearbeiten",
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(20.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text("Bearbeiten", color = MaterialTheme.colorScheme.primary)
}
}
)
DropdownMenuItem(
onClick = {
showMenu = false
onDeleteNote()
},
text = {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "Löschen",
tint = MaterialTheme.colorScheme.error,
modifier = Modifier.size(20.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text("Löschen", color = MaterialTheme.colorScheme.error)
}
}
)
}
}
}
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun NoteGrid(
@ -283,9 +164,11 @@ fun NoteGrid(
onCollectionClick: (NoteCollection) -> Unit,
onNoteClick: (NoteEntity) -> Unit,
onDeleteNote: (NoteEntity) -> Unit,
onEditNote: (NoteEntity) ->Unit
onEditNote: (NoteEntity) ->Unit,
onDeleteCollection: (NoteCollection) -> Unit
) {
val columns = 2 // Anzahl Spalten, ggf. responsiv machen
var showCollectionConfirmationDialog by remember { mutableStateOf(false) }
var deletedCollection: NoteCollection? by remember { mutableStateOf(null) }
LazyVerticalGrid(
modifier = modifier.fillMaxSize(),
columns = GridCells.Adaptive(minSize = 240.dp),
@ -294,37 +177,22 @@ fun NoteGrid(
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
items(collections) { collection ->
Column(
modifier = Modifier
.height(120.dp)
.fillMaxWidth()
.clip(RoundedCornerShape(12.dp))
.background(MaterialTheme.colorScheme.surfaceVariant)
.clickable { onCollectionClick(collection) }
.padding(12.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
imageVector = Icons.Filled.Folder,
contentDescription = "Ordner",
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(64.dp)
)
Text(
text = collection.name,
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(top = 8.dp)
)
}
CollectionItem(collection, onDeleteCollection = {
showCollectionConfirmationDialog = true
deletedCollection = collection
})
}
items(notes) { note ->
NoteItem(note, onNoteClick = {}, onEditTitle = {onEditNote(note)}, onDeleteNote = {onDeleteNote(note)})
}
}
if(showCollectionConfirmationDialog && deletedCollection != null) {
DeleteCollectionDialog(deletedCollection!!.name , {}, {
onDeleteCollection(deletedCollection!!)
showCollectionConfirmationDialog = false
})
}
}
@Composable
fun NotesScreen(
@ -402,7 +270,10 @@ fun NotesScreen(
onDeleteNote = {
viewModel.removeNote(noteEntity = it)
},
onEditNote = onEditNote
onEditNote = onEditNote,
onDeleteCollection = {
viewModel.removeCollection(noteCollection = it)
}
)
}

View File

@ -3,6 +3,7 @@ package come.stormborntales.notevault.ui.viewmodel
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import come.stormborntales.notevault.data.local.entity.NoteCollection
import come.stormborntales.notevault.data.local.entity.NoteEntity
import come.stormborntales.notevault.data.repository.CollectionRepository
@ -68,6 +69,12 @@ class NoteBrowserViewModel (
}
}
fun removeCollection(noteCollection: NoteCollection) {
viewModelScope.launch {
collectionRepository.deleteCollection(noteCollection)
}
}
fun navigateToLevel(index: Int) {
val newStack = _pathStack.value.take(index + 1)
_pathStack.value = newStack
@ -81,4 +88,6 @@ class NoteBrowserViewModel (
val newParentId = newStack.lastOrNull()?.id
loadContentForParent(newParentId)
}
}