ADD: Dialog Composable to add new Collections and ViewModel for NoteBrowser
This commit is contained in:
parent
d6588913fd
commit
4ff09a6759
@ -42,6 +42,7 @@ import androidx.navigation.compose.currentBackStackEntryAsState
|
|||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import come.stormborntales.notevault.data.local.AppDatabase
|
import come.stormborntales.notevault.data.local.AppDatabase
|
||||||
import come.stormborntales.notevault.data.local.entity.NoteEntity
|
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.data.repository.NoteRepository
|
||||||
import come.stormborntales.notevault.ui.screens.AddNoteDialog
|
import come.stormborntales.notevault.ui.screens.AddNoteDialog
|
||||||
import come.stormborntales.notevault.ui.screens.MainScreen
|
import come.stormborntales.notevault.ui.screens.MainScreen
|
||||||
@ -59,8 +60,9 @@ class MainActivity : ComponentActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
val applicationContext = applicationContext
|
val applicationContext = applicationContext
|
||||||
val database = AppDatabase.getDatabase(applicationContext)
|
val database = AppDatabase.getDatabase(applicationContext)
|
||||||
val repository = NoteRepository(database.noteDao())
|
val noteRepository = NoteRepository(database.noteDao())
|
||||||
val viewModelFactory = NoteViewModelFactory(repository)
|
val collectionRepository = CollectionRepository(database.collectionDao())
|
||||||
|
val viewModelFactory = NoteViewModelFactory(noteRepository)
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
NoteVaultTheme {
|
NoteVaultTheme {
|
||||||
@ -206,7 +208,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
SettingsScreen()
|
SettingsScreen()
|
||||||
}
|
}
|
||||||
composable("notes") {
|
composable("notes") {
|
||||||
NotesScreen()
|
NotesScreen(collectionRepository, noteRepository)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import androidx.room.Database
|
|||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
import androidx.room.TypeConverters
|
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.dao.NoteDao
|
||||||
import come.stormborntales.notevault.data.local.entity.NoteCollection
|
import come.stormborntales.notevault.data.local.entity.NoteCollection
|
||||||
import come.stormborntales.notevault.data.local.entity.NoteEntity
|
import come.stormborntales.notevault.data.local.entity.NoteEntity
|
||||||
@ -14,6 +15,7 @@ import come.stormborntales.notevault.data.local.entity.NoteEntity
|
|||||||
@TypeConverters(UriListConverter::class)
|
@TypeConverters(UriListConverter::class)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
abstract fun noteDao(): NoteDao
|
abstract fun noteDao(): NoteDao
|
||||||
|
abstract fun collectionDao(): CollectionDao
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@Volatile
|
@Volatile
|
||||||
|
@ -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>>
|
||||||
|
}
|
@ -22,4 +22,6 @@ interface NoteDao {
|
|||||||
|
|
||||||
@Update
|
@Update
|
||||||
suspend fun update(note: NoteEntity)
|
suspend fun update(note: NoteEntity)
|
||||||
|
@Query("SELECT * FROM notes WHERE collectionId = :collectionId")
|
||||||
|
fun getNotesForCollection(collectionId: Int?): Flow<List<NoteEntity>>
|
||||||
}
|
}
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
@ -11,4 +11,5 @@ class NoteRepository(private val dao: NoteDao) {
|
|||||||
suspend fun delete(note: NoteEntity) = dao.delete(note)
|
suspend fun delete(note: NoteEntity) = dao.delete(note)
|
||||||
|
|
||||||
suspend fun update(note: NoteEntity) = dao.update(note);
|
suspend fun update(note: NoteEntity) = dao.update(note);
|
||||||
|
fun getNotesForCollection(parentId: Int?) = dao.getNotesForCollection(parentId);
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,111 @@
|
|||||||
package come.stormborntales.notevault.ui.screens
|
package come.stormborntales.notevault.ui.screens
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
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.wrapContentWidth
|
import androidx.compose.foundation.layout.wrapContentWidth
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.FloatingActionButton
|
import androidx.compose.material3.FloatingActionButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
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
|
@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 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(
|
Scaffold(
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
@ -37,14 +121,14 @@ fun NotesScreen() {
|
|||||||
.wrapContentWidth()
|
.wrapContentWidth()
|
||||||
) {
|
) {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text("Option 1") },
|
text = { Text("Ordner erstellen") },
|
||||||
onClick = {
|
onClick = {
|
||||||
menuExpanded = false
|
menuExpanded = false
|
||||||
// Handle action
|
showDialog = true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text("Option 2") },
|
text = { Text("Noten importieren") },
|
||||||
onClick = {
|
onClick = {
|
||||||
menuExpanded = false
|
menuExpanded = false
|
||||||
// Handle action
|
// Handle action
|
||||||
@ -60,6 +144,16 @@ fun NotesScreen() {
|
|||||||
.padding(innerPadding)) {
|
.padding(innerPadding)) {
|
||||||
Text("Inhalt deiner App", modifier = Modifier.align(Alignment.Center))
|
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")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user