ADD: Dialog Composable to add new Collections and ViewModel for NoteBrowser

This commit is contained in:
sebastian 2025-05-10 10:50:46 +02:00
parent d6588913fd
commit 4ff09a6759
9 changed files with 248 additions and 7 deletions

View File

@ -42,6 +42,7 @@ import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import come.stormborntales.notevault.data.local.AppDatabase
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.screens.AddNoteDialog
import come.stormborntales.notevault.ui.screens.MainScreen
@ -59,8 +60,9 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
val applicationContext = applicationContext
val database = AppDatabase.getDatabase(applicationContext)
val repository = NoteRepository(database.noteDao())
val viewModelFactory = NoteViewModelFactory(repository)
val noteRepository = NoteRepository(database.noteDao())
val collectionRepository = CollectionRepository(database.collectionDao())
val viewModelFactory = NoteViewModelFactory(noteRepository)
setContent {
NoteVaultTheme {
@ -206,7 +208,7 @@ class MainActivity : ComponentActivity() {
SettingsScreen()
}
composable("notes") {
NotesScreen()
NotesScreen(collectionRepository, noteRepository)
}
}

View File

@ -6,6 +6,7 @@ import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import come.stormborntales.notevault.data.local.dao.CollectionDao
import come.stormborntales.notevault.data.local.dao.NoteDao
import come.stormborntales.notevault.data.local.entity.NoteCollection
import come.stormborntales.notevault.data.local.entity.NoteEntity
@ -14,6 +15,7 @@ import come.stormborntales.notevault.data.local.entity.NoteEntity
@TypeConverters(UriListConverter::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun noteDao(): NoteDao
abstract fun collectionDao(): CollectionDao
companion object {
@Volatile

View File

@ -0,0 +1,32 @@
package come.stormborntales.notevault.data.local.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import come.stormborntales.notevault.data.local.entity.NoteCollection
import kotlinx.coroutines.flow.Flow
@Dao
interface CollectionDao {
@Query(value = "SELECT * FROM note_collections WHERE parentId IS :parentId")
fun getCollectionsByParent(parentId: Int?): Flow<List<NoteCollection>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertCollection(collection: NoteCollection)
@Update
suspend fun updateCollection(collection: NoteCollection)
@Delete
suspend fun deleteCollection(collection: NoteCollection)
@Query("SELECT * FROM note_collections WHERE id = :id LIMIT 1")
suspend fun getById(id: Int): NoteCollection?
@Query("SELECT * FROM note_collections")
fun getAll(): Flow<List<NoteCollection>>
}

View File

@ -22,4 +22,6 @@ interface NoteDao {
@Update
suspend fun update(note: NoteEntity)
@Query("SELECT * FROM notes WHERE collectionId = :collectionId")
fun getNotesForCollection(collectionId: Int?): Flow<List<NoteEntity>>
}

View File

@ -0,0 +1,34 @@
package come.stormborntales.notevault.data.repository
import come.stormborntales.notevault.data.local.dao.CollectionDao
import come.stormborntales.notevault.data.local.dao.NoteDao
import come.stormborntales.notevault.data.local.entity.NoteCollection
import come.stormborntales.notevault.data.local.entity.NoteEntity
import kotlinx.coroutines.flow.Flow
class CollectionRepository(private val dao: CollectionDao) {
fun getCollectionsByParent(parentId: Int?): Flow<List<NoteCollection>> {
return dao.getCollectionsByParent(parentId)
}
suspend fun insertCollection(collection: NoteCollection) {
dao.insertCollection(collection)
}
suspend fun updateCollection(collection: NoteCollection) {
dao.updateCollection(collection)
}
suspend fun deleteCollection(collection: NoteCollection) {
dao.deleteCollection(collection)
}
suspend fun getCollectionById(id: Int): NoteCollection? {
return dao.getById(id)
}
fun getAllCollections(): Flow<List<NoteCollection>> {
return dao.getAll()
}
}

View File

@ -11,4 +11,5 @@ class NoteRepository(private val dao: NoteDao) {
suspend fun delete(note: NoteEntity) = dao.delete(note)
suspend fun update(note: NoteEntity) = dao.update(note);
fun getNotesForCollection(parentId: Int?) = dao.getNotesForCollection(parentId);
}

View File

@ -1,27 +1,111 @@
package come.stormborntales.notevault.ui.screens
import android.util.Log
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.wrapContentWidth
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
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.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
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.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import androidx.lifecycle.viewmodel.compose.viewModel
import come.stormborntales.notevault.data.local.AppDatabase
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 NotesScreen() {
fun CreateCollectionDialog(
onDismiss: () -> Unit,
onCreate: (String) -> Unit
) {
var collectionName by remember { mutableStateOf("") }
var isError by remember { mutableStateOf(false) }
AlertDialog(
onDismissRequest = onDismiss,
title = {
Text("Neue Collection erstellen")
},
text = {
Column {
OutlinedTextField(
value = collectionName,
onValueChange = {
collectionName = it
isError = false
},
label = { Text("Collection-Name") },
isError = isError,
singleLine = true
)
if (isError) {
Text(
text = "Name darf nicht leer sein",
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodySmall
)
}
}
},
confirmButton = {
TextButton(onClick = {
if (collectionName.isBlank()) {
isError = true
} else {
onCreate(collectionName.trim())
onDismiss()
}
}) {
Text("Erstellen")
}
},
dismissButton = {
TextButton(onClick = onDismiss) {
Text("Abbrechen")
}
}
)
}
@Composable
fun NotesScreen(
collectionRepository: CollectionRepository,
noteRepository: NoteRepository,
) {
var menuExpanded by remember { mutableStateOf(false) }
var showDialog by remember { mutableStateOf(false) }
val viewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current)
val viewModel: NoteBrowserViewModel = viewModel(
viewModelStoreOwner,
factory = NoteBrowserViewModelFactory(noteRepository, collectionRepository)
)
val collections by viewModel.currentCollections.collectAsState()
val notes by viewModel.currentNotes.collectAsState()
Scaffold(
floatingActionButton = {
@ -37,14 +121,14 @@ fun NotesScreen() {
.wrapContentWidth()
) {
DropdownMenuItem(
text = { Text("Option 1") },
text = { Text("Ordner erstellen") },
onClick = {
menuExpanded = false
// Handle action
showDialog = true
}
)
DropdownMenuItem(
text = { Text("Option 2") },
text = { Text("Noten importieren") },
onClick = {
menuExpanded = false
// Handle action
@ -60,6 +144,16 @@ fun NotesScreen() {
.padding(innerPadding)) {
Text("Inhalt deiner App", modifier = Modifier.align(Alignment.Center))
}
if (showDialog) {
CreateCollectionDialog(
onDismiss = { showDialog = false },
onCreate = { name ->
// Handle the new collection name
Log.d("NotesBrowser", "onCreate: $name")
}
)
}
}
)
}

View File

@ -0,0 +1,54 @@
package come.stormborntales.notevault.ui.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
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 kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
class NoteBrowserViewModel (
private val noteRepository: NoteRepository,
private val collectionRepository: CollectionRepository
) : ViewModel() {
private val _currentCollections = MutableStateFlow<List<NoteCollection>>(emptyList())
val currentCollections: StateFlow<List<NoteCollection>> = _currentCollections.asStateFlow()
private val _currentNotes = MutableStateFlow<List<NoteEntity>>(emptyList())
val currentNotes: StateFlow<List<NoteEntity>> = _currentNotes.asStateFlow()
private val _currentParentId = MutableStateFlow<Int?>(null)
val currentParentId: StateFlow<Int?> = _currentParentId.asStateFlow()
init {
loadContentForParent(null)
}
fun loadContentForParent(parentId: Int?) {
_currentParentId.value = parentId
viewModelScope.launch {
launch {
collectionRepository.getCollectionsByParent(parentId).collect {
_currentCollections.value = it
}
}
launch {
noteRepository.getNotesForCollection(parentId).collect {
_currentNotes.value = it
}
}
}
}
fun createCollection(name: String) {
viewModelScope.launch {
val newCollection = NoteCollection(name = name, parentId = _currentParentId.value)
collectionRepository.insertCollection(newCollection)
loadContentForParent(_currentParentId.value)
}
}
}

View File

@ -0,0 +1,20 @@
package come.stormborntales.notevault.ui.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import come.stormborntales.notevault.data.repository.CollectionRepository
import come.stormborntales.notevault.data.repository.NoteRepository
@Suppress("UNCHECKED_CAST")
class NoteBrowserViewModelFactory (
private val noteRepository: NoteRepository,
private val collectionRepository: CollectionRepository
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(NoteBrowserViewModel::class.java)) {
return NoteBrowserViewModel(noteRepository, collectionRepository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}