diff --git a/app/src/main/java/come/stormborntales/notevault/MainActivity.kt b/app/src/main/java/come/stormborntales/notevault/MainActivity.kt index 3b6ce01..858cf87 100644 --- a/app/src/main/java/come/stormborntales/notevault/MainActivity.kt +++ b/app/src/main/java/come/stormborntales/notevault/MainActivity.kt @@ -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) } } diff --git a/app/src/main/java/come/stormborntales/notevault/data/local/AppDatabase.kt b/app/src/main/java/come/stormborntales/notevault/data/local/AppDatabase.kt index ea9cf37..527cba5 100644 --- a/app/src/main/java/come/stormborntales/notevault/data/local/AppDatabase.kt +++ b/app/src/main/java/come/stormborntales/notevault/data/local/AppDatabase.kt @@ -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 diff --git a/app/src/main/java/come/stormborntales/notevault/data/local/dao/CollectionDao.kt b/app/src/main/java/come/stormborntales/notevault/data/local/dao/CollectionDao.kt new file mode 100644 index 0000000..bc85d7e --- /dev/null +++ b/app/src/main/java/come/stormborntales/notevault/data/local/dao/CollectionDao.kt @@ -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> + + @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> +} \ No newline at end of file diff --git a/app/src/main/java/come/stormborntales/notevault/data/local/dao/NoteDao.kt b/app/src/main/java/come/stormborntales/notevault/data/local/dao/NoteDao.kt index a9c55ae..df74f90 100644 --- a/app/src/main/java/come/stormborntales/notevault/data/local/dao/NoteDao.kt +++ b/app/src/main/java/come/stormborntales/notevault/data/local/dao/NoteDao.kt @@ -22,4 +22,6 @@ interface NoteDao { @Update suspend fun update(note: NoteEntity) + @Query("SELECT * FROM notes WHERE collectionId = :collectionId") + fun getNotesForCollection(collectionId: Int?): Flow> } \ No newline at end of file diff --git a/app/src/main/java/come/stormborntales/notevault/data/repository/CollectionRepository.kt b/app/src/main/java/come/stormborntales/notevault/data/repository/CollectionRepository.kt new file mode 100644 index 0000000..47433b4 --- /dev/null +++ b/app/src/main/java/come/stormborntales/notevault/data/repository/CollectionRepository.kt @@ -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> { + 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> { + return dao.getAll() + } +} \ No newline at end of file diff --git a/app/src/main/java/come/stormborntales/notevault/data/repository/NoteRepository.kt b/app/src/main/java/come/stormborntales/notevault/data/repository/NoteRepository.kt index 55ac247..0464fd3 100644 --- a/app/src/main/java/come/stormborntales/notevault/data/repository/NoteRepository.kt +++ b/app/src/main/java/come/stormborntales/notevault/data/repository/NoteRepository.kt @@ -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); } diff --git a/app/src/main/java/come/stormborntales/notevault/ui/screens/NotesBrowser.kt b/app/src/main/java/come/stormborntales/notevault/ui/screens/NotesBrowser.kt index 16a7c0a..8af9b44 100644 --- a/app/src/main/java/come/stormborntales/notevault/ui/screens/NotesBrowser.kt +++ b/app/src/main/java/come/stormborntales/notevault/ui/screens/NotesBrowser.kt @@ -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") + } + ) + } } ) } diff --git a/app/src/main/java/come/stormborntales/notevault/ui/viewmodel/NoteBrowserViewModel.kt b/app/src/main/java/come/stormborntales/notevault/ui/viewmodel/NoteBrowserViewModel.kt new file mode 100644 index 0000000..73eb804 --- /dev/null +++ b/app/src/main/java/come/stormborntales/notevault/ui/viewmodel/NoteBrowserViewModel.kt @@ -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>(emptyList()) + val currentCollections: StateFlow> = _currentCollections.asStateFlow() + + private val _currentNotes = MutableStateFlow>(emptyList()) + val currentNotes: StateFlow> = _currentNotes.asStateFlow() + + private val _currentParentId = MutableStateFlow(null) + val currentParentId: StateFlow = _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) + } + } +} diff --git a/app/src/main/java/come/stormborntales/notevault/ui/viewmodel/NoteBrowserViewModelFactory.kt b/app/src/main/java/come/stormborntales/notevault/ui/viewmodel/NoteBrowserViewModelFactory.kt new file mode 100644 index 0000000..80b9a4f --- /dev/null +++ b/app/src/main/java/come/stormborntales/notevault/ui/viewmodel/NoteBrowserViewModelFactory.kt @@ -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 create(modelClass: Class): T { + if (modelClass.isAssignableFrom(NoteBrowserViewModel::class.java)) { + return NoteBrowserViewModel(noteRepository, collectionRepository) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} \ No newline at end of file