Compare commits
	
		
			No commits in common. "nextNoteVault" and "compose-integration" have entirely different histories.
		
	
	
		
			nextNoteVa
			...
			compose-in
		
	
		
							
								
								
									
										1
									
								
								.idea/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.idea/.gitignore
									
									
									
									
										vendored
									
									
								
							@ -6,4 +6,3 @@
 | 
				
			|||||||
# Datasource local storage ignored files
 | 
					# Datasource local storage ignored files
 | 
				
			||||||
/dataSources/
 | 
					/dataSources/
 | 
				
			||||||
/dataSources.local.xml
 | 
					/dataSources.local.xml
 | 
				
			||||||
/AndroidProjectSystem.xml
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,49 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
<project version="4">
 | 
					<project version="4">
 | 
				
			||||||
  <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
 | 
					  <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
 | 
				
			||||||
    <data-source source="LOCAL" name="note_database" uuid="669634a7-d50e-4c04-b2fe-786d18bbd166">
 | 
					    <data-source source="LOCAL" name="music_database" uuid="95a3c2ec-2c29-4336-900a-3993de90ae66">
 | 
				
			||||||
 | 
					      <driver-ref>sqlite.xerial</driver-ref>
 | 
				
			||||||
 | 
					      <synchronize>true</synchronize>
 | 
				
			||||||
 | 
					      <jdbc-driver>org.sqlite.JDBC</jdbc-driver>
 | 
				
			||||||
 | 
					      <jdbc-url>jdbc:sqlite:$USER_HOME$/.cache/JetBrains/IntelliJIdea2025.1/device-explorer/samsung SM-P610/_/data/data/com.stormtales.notevault/databases/music_database</jdbc-url>
 | 
				
			||||||
 | 
					      <jdbc-additional-properties>
 | 
				
			||||||
 | 
					        <property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
 | 
				
			||||||
 | 
					      </jdbc-additional-properties>
 | 
				
			||||||
 | 
					      <working-dir>$ProjectFileDir$</working-dir>
 | 
				
			||||||
 | 
					      <libraries>
 | 
				
			||||||
 | 
					        <library>
 | 
				
			||||||
 | 
					          <url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/xerial/sqlite-jdbc/3.45.1.0/sqlite-jdbc-3.45.1.0.jar</url>
 | 
				
			||||||
 | 
					        </library>
 | 
				
			||||||
 | 
					        <library>
 | 
				
			||||||
 | 
					          <url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar</url>
 | 
				
			||||||
 | 
					        </library>
 | 
				
			||||||
 | 
					      </libraries>
 | 
				
			||||||
 | 
					    </data-source>
 | 
				
			||||||
 | 
					    <data-source source="LOCAL" name="note_database" uuid="b3770d7c-0a73-40c6-aab8-010effaa19b6">
 | 
				
			||||||
 | 
					      <driver-ref>sqlite.xerial</driver-ref>
 | 
				
			||||||
 | 
					      <synchronize>true</synchronize>
 | 
				
			||||||
 | 
					      <jdbc-driver>org.sqlite.JDBC</jdbc-driver>
 | 
				
			||||||
 | 
					      <jdbc-url>jdbc:sqlite:$USER_HOME$/.cache/JetBrains/IntelliJIdea2025.1/device-explorer/samsung SM-P610/_/data/data/come.stormborntales.notevault/databases/note_database</jdbc-url>
 | 
				
			||||||
 | 
					      <jdbc-additional-properties>
 | 
				
			||||||
 | 
					        <property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
 | 
				
			||||||
 | 
					      </jdbc-additional-properties>
 | 
				
			||||||
 | 
					      <working-dir>$ProjectFileDir$</working-dir>
 | 
				
			||||||
 | 
					      <libraries>
 | 
				
			||||||
 | 
					        <library>
 | 
				
			||||||
 | 
					          <url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/xerial/sqlite-jdbc/3.45.1.0/sqlite-jdbc-3.45.1.0.jar</url>
 | 
				
			||||||
 | 
					        </library>
 | 
				
			||||||
 | 
					        <library>
 | 
				
			||||||
 | 
					          <url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar</url>
 | 
				
			||||||
 | 
					        </library>
 | 
				
			||||||
 | 
					        <library>
 | 
				
			||||||
 | 
					          <url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/xerial/sqlite-jdbc/3.45.1.0/sqlite-jdbc-3.45.1.0.jar</url>
 | 
				
			||||||
 | 
					        </library>
 | 
				
			||||||
 | 
					        <library>
 | 
				
			||||||
 | 
					          <url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar</url>
 | 
				
			||||||
 | 
					        </library>
 | 
				
			||||||
 | 
					      </libraries>
 | 
				
			||||||
 | 
					    </data-source>
 | 
				
			||||||
 | 
					    <data-source source="LOCAL" name="note_database [2]" uuid="ad94cfd9-e485-4151-8bf4-a080c51fa27c">
 | 
				
			||||||
      <driver-ref>sqlite.xerial</driver-ref>
 | 
					      <driver-ref>sqlite.xerial</driver-ref>
 | 
				
			||||||
      <synchronize>true</synchronize>
 | 
					      <synchronize>true</synchronize>
 | 
				
			||||||
      <jdbc-driver>org.sqlite.JDBC</jdbc-driver>
 | 
					      <jdbc-driver>org.sqlite.JDBC</jdbc-driver>
 | 
				
			||||||
@ -23,12 +65,6 @@
 | 
				
			|||||||
        <library>
 | 
					        <library>
 | 
				
			||||||
          <url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar</url>
 | 
					          <url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar</url>
 | 
				
			||||||
        </library>
 | 
					        </library>
 | 
				
			||||||
        <library>
 | 
					 | 
				
			||||||
          <url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/xerial/sqlite-jdbc/3.45.1.0/sqlite-jdbc-3.45.1.0.jar</url>
 | 
					 | 
				
			||||||
        </library>
 | 
					 | 
				
			||||||
        <library>
 | 
					 | 
				
			||||||
          <url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar</url>
 | 
					 | 
				
			||||||
        </library>
 | 
					 | 
				
			||||||
      </libraries>
 | 
					      </libraries>
 | 
				
			||||||
    </data-source>
 | 
					    </data-source>
 | 
				
			||||||
  </component>
 | 
					  </component>
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@
 | 
				
			|||||||
    <selectionStates>
 | 
					    <selectionStates>
 | 
				
			||||||
      <SelectionState runConfigName="app">
 | 
					      <SelectionState runConfigName="app">
 | 
				
			||||||
        <option name="selectionMode" value="DROPDOWN" />
 | 
					        <option name="selectionMode" value="DROPDOWN" />
 | 
				
			||||||
        <DropdownSelection timestamp="2025-05-10T10:58:30.749991183Z">
 | 
					        <DropdownSelection timestamp="2025-04-29T17:33:41.575251940Z">
 | 
				
			||||||
          <Target type="DEFAULT_BOOT">
 | 
					          <Target type="DEFAULT_BOOT">
 | 
				
			||||||
            <handle>
 | 
					            <handle>
 | 
				
			||||||
              <DeviceId pluginId="PhysicalDevice" identifier="serial=R52N50NLGRT" />
 | 
					              <DeviceId pluginId="PhysicalDevice" identifier="serial=R52N50NLGRT" />
 | 
				
			||||||
 | 
				
			|||||||
@ -40,9 +40,6 @@
 | 
				
			|||||||
    <inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
 | 
					    <inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
 | 
				
			||||||
      <option name="composableFile" value="true" />
 | 
					      <option name="composableFile" value="true" />
 | 
				
			||||||
    </inspection_tool>
 | 
					    </inspection_tool>
 | 
				
			||||||
    <inspection_tool class="PreviewParameterProviderOnFirstParameter" enabled="true" level="ERROR" enabled_by_default="true">
 | 
					 | 
				
			||||||
      <option name="composableFile" value="true" />
 | 
					 | 
				
			||||||
    </inspection_tool>
 | 
					 | 
				
			||||||
    <inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
 | 
					    <inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
 | 
				
			||||||
      <option name="composableFile" value="true" />
 | 
					      <option name="composableFile" value="true" />
 | 
				
			||||||
    </inspection_tool>
 | 
					    </inspection_tool>
 | 
				
			||||||
 | 
				
			|||||||
@ -63,5 +63,4 @@ dependencies {
 | 
				
			|||||||
    implementation(libs.compose.runtime.livedata)
 | 
					    implementation(libs.compose.runtime.livedata)
 | 
				
			||||||
    implementation(libs.coil.compose)
 | 
					    implementation(libs.coil.compose)
 | 
				
			||||||
    ksp(libs.androidx.room.compiler)
 | 
					    ksp(libs.androidx.room.compiler)
 | 
				
			||||||
    implementation(libs.androidx.material.icons.extended)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -2,82 +2,49 @@ package come.stormborntales.notevault
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import android.net.Uri
 | 
					import android.net.Uri
 | 
				
			||||||
import android.os.Bundle
 | 
					import android.os.Bundle
 | 
				
			||||||
import android.util.Log
 | 
					import android.webkit.MimeTypeMap
 | 
				
			||||||
import androidx.activity.ComponentActivity
 | 
					import androidx.activity.ComponentActivity
 | 
				
			||||||
import androidx.activity.compose.rememberLauncherForActivityResult
 | 
					import androidx.activity.compose.rememberLauncherForActivityResult
 | 
				
			||||||
import androidx.activity.compose.setContent
 | 
					import androidx.activity.compose.setContent
 | 
				
			||||||
 | 
					import androidx.activity.result.PickVisualMediaRequest
 | 
				
			||||||
import androidx.activity.result.contract.ActivityResultContracts
 | 
					import androidx.activity.result.contract.ActivityResultContracts
 | 
				
			||||||
import androidx.compose.foundation.layout.Box
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.fillMaxWidth
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.padding
 | 
					 | 
				
			||||||
import androidx.compose.material.icons.Icons
 | 
					 | 
				
			||||||
import androidx.compose.material.icons.filled.AccountCircle
 | 
					 | 
				
			||||||
import androidx.compose.material.icons.filled.Menu
 | 
					 | 
				
			||||||
import androidx.compose.material3.DrawerValue
 | 
					 | 
				
			||||||
import androidx.compose.material3.DropdownMenu
 | 
					 | 
				
			||||||
import androidx.compose.material3.DropdownMenuItem
 | 
					 | 
				
			||||||
import androidx.compose.material3.ExperimentalMaterial3Api
 | 
					 | 
				
			||||||
import androidx.compose.material3.Icon
 | 
					 | 
				
			||||||
import androidx.compose.material3.IconButton
 | 
					 | 
				
			||||||
import androidx.compose.material3.MaterialTheme
 | 
					 | 
				
			||||||
import androidx.compose.material3.ModalDrawerSheet
 | 
					 | 
				
			||||||
import androidx.compose.material3.ModalNavigationDrawer
 | 
					 | 
				
			||||||
import androidx.compose.material3.NavigationDrawerItem
 | 
					 | 
				
			||||||
import androidx.compose.material3.NavigationDrawerItemDefaults
 | 
					 | 
				
			||||||
import androidx.compose.material3.Scaffold
 | 
					 | 
				
			||||||
import androidx.compose.material3.Text
 | 
					 | 
				
			||||||
import androidx.compose.material3.TextField
 | 
					 | 
				
			||||||
import androidx.compose.material3.TextFieldDefaults
 | 
					 | 
				
			||||||
import androidx.compose.material3.TopAppBar
 | 
					 | 
				
			||||||
import androidx.compose.material3.rememberDrawerState
 | 
					 | 
				
			||||||
import androidx.compose.runtime.*
 | 
					import androidx.compose.runtime.*
 | 
				
			||||||
import androidx.compose.ui.Modifier
 | 
					 | 
				
			||||||
import androidx.compose.ui.graphics.Color
 | 
					 | 
				
			||||||
import androidx.compose.ui.platform.LocalContext
 | 
					import androidx.compose.ui.platform.LocalContext
 | 
				
			||||||
import androidx.compose.ui.unit.dp
 | 
					 | 
				
			||||||
import androidx.lifecycle.viewmodel.compose.viewModel
 | 
					import androidx.lifecycle.viewmodel.compose.viewModel
 | 
				
			||||||
import androidx.navigation.compose.NavHost
 | 
					 | 
				
			||||||
import androidx.navigation.compose.composable
 | 
					 | 
				
			||||||
import androidx.navigation.compose.currentBackStackEntryAsState
 | 
					 | 
				
			||||||
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.model.NoteEntry
 | 
				
			||||||
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
 | 
				
			||||||
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.theme.NoteVaultTheme
 | 
				
			||||||
import come.stormborntales.notevault.ui.viewmodel.NoteBrowserViewModel
 | 
					 | 
				
			||||||
import come.stormborntales.notevault.ui.viewmodel.NoteBrowserViewModelFactory
 | 
					 | 
				
			||||||
import come.stormborntales.notevault.ui.viewmodel.NoteViewModel
 | 
					import come.stormborntales.notevault.ui.viewmodel.NoteViewModel
 | 
				
			||||||
import come.stormborntales.notevault.ui.viewmodel.NoteViewModelFactory
 | 
					import come.stormborntales.notevault.ui.viewmodel.NoteViewModelFactory
 | 
				
			||||||
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
import kotlinx.coroutines.launch
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
 | 
					import java.io.File
 | 
				
			||||||
 | 
					import java.io.InputStream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MainActivity : ComponentActivity() {
 | 
					class MainActivity : ComponentActivity() {
 | 
				
			||||||
    @OptIn(ExperimentalMaterial3Api::class)
 | 
					 | 
				
			||||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
					    override fun onCreate(savedInstanceState: Bundle?) {
 | 
				
			||||||
        super.onCreate(savedInstanceState)
 | 
					        super.onCreate(savedInstanceState)
 | 
				
			||||||
        val applicationContext = applicationContext
 | 
					        val applicationContext = applicationContext
 | 
				
			||||||
        val database = AppDatabase.getDatabase(applicationContext)
 | 
					        val database = AppDatabase.getDatabase(applicationContext)
 | 
				
			||||||
        val noteRepository = NoteRepository(database.noteDao())
 | 
					        val repository = NoteRepository(database.noteDao())
 | 
				
			||||||
        val collectionRepository = CollectionRepository(database.collectionDao())
 | 
					        val viewModelFactory = NoteViewModelFactory(repository)
 | 
				
			||||||
        val viewModelFactory = NoteViewModelFactory(noteRepository)
 | 
					 | 
				
			||||||
        val noteBrowserViewModelFactory = NoteBrowserViewModelFactory(noteRepository, collectionRepository)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        setContent {
 | 
					        setContent {
 | 
				
			||||||
            NoteVaultTheme {
 | 
					            NoteVaultTheme {
 | 
				
			||||||
                val navController = rememberNavController()
 | 
					 | 
				
			||||||
                val viewModel: NoteViewModel = viewModel(factory = viewModelFactory)
 | 
					                val viewModel: NoteViewModel = viewModel(factory = viewModelFactory)
 | 
				
			||||||
                val noteBrowserViewModel: NoteBrowserViewModel = viewModel(factory = noteBrowserViewModelFactory)
 | 
					                val context = LocalContext.current
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Globale Notenliste
 | 
				
			||||||
 | 
					                val notes = remember { mutableStateListOf<NoteEntry>() }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Bildauswahl + Dialog-States
 | 
				
			||||||
                var selectedUris by remember { mutableStateOf<List<Uri>>(emptyList()) }
 | 
					                var selectedUris by remember { mutableStateOf<List<Uri>>(emptyList()) }
 | 
				
			||||||
                var showDialog by remember { mutableStateOf(false) }
 | 
					                var showDialog by remember { mutableStateOf(false) }
 | 
				
			||||||
                var noteToEdit by remember { mutableStateOf<NoteEntity?>(null) }
 | 
					 | 
				
			||||||
                val drawerState = rememberDrawerState(DrawerValue.Closed)
 | 
					 | 
				
			||||||
                val scope = rememberCoroutineScope()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Launcher innerhalb von Compose
 | 
				
			||||||
                val imagePickerLauncher = rememberLauncherForActivityResult(
 | 
					                val imagePickerLauncher = rememberLauncherForActivityResult(
 | 
				
			||||||
                    contract = ActivityResultContracts.OpenMultipleDocuments(),
 | 
					                    contract = ActivityResultContracts.OpenMultipleDocuments(),
 | 
				
			||||||
                    onResult = { uris ->
 | 
					                    onResult = { uris ->
 | 
				
			||||||
@ -88,145 +55,23 @@ class MainActivity : ComponentActivity() {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                val openDialog: (NoteEntity?) -> Unit = { note ->
 | 
					                // UI anzeigen
 | 
				
			||||||
                    Log.d("EditNote", "NoteEntity: ${note?.title}")
 | 
					 | 
				
			||||||
                    noteToEdit = note
 | 
					 | 
				
			||||||
                    showDialog = true
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                val navItems = listOf(
 | 
					 | 
				
			||||||
                    "Home" to "main",
 | 
					 | 
				
			||||||
                    "Notes" to "notes",
 | 
					 | 
				
			||||||
                    "Einstellungen" to "settings"
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                ModalNavigationDrawer(
 | 
					 | 
				
			||||||
                    drawerState = drawerState,
 | 
					 | 
				
			||||||
                    drawerContent = {
 | 
					 | 
				
			||||||
                        ModalDrawerSheet {
 | 
					 | 
				
			||||||
                            Text("Navigation", modifier = Modifier.padding(16.dp), style = MaterialTheme.typography.titleMedium)
 | 
					 | 
				
			||||||
                            navItems.forEach { (label, route) ->
 | 
					 | 
				
			||||||
                                NavigationDrawerItem(
 | 
					 | 
				
			||||||
                                    label = { Text(label) },
 | 
					 | 
				
			||||||
                                    selected = false,
 | 
					 | 
				
			||||||
                                    onClick = {
 | 
					 | 
				
			||||||
                                        navController.navigate(route) {
 | 
					 | 
				
			||||||
                                            popUpTo(navController.graph.startDestinationId) { saveState = true }
 | 
					 | 
				
			||||||
                                            launchSingleTop = true
 | 
					 | 
				
			||||||
                                            restoreState = true
 | 
					 | 
				
			||||||
                                        }
 | 
					 | 
				
			||||||
                                        scope.launch { drawerState.close() }
 | 
					 | 
				
			||||||
                                    },
 | 
					 | 
				
			||||||
                                    modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
 | 
					 | 
				
			||||||
                                )
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                ) {
 | 
					 | 
				
			||||||
                    Scaffold(
 | 
					 | 
				
			||||||
                        topBar = {
 | 
					 | 
				
			||||||
                            var searchQuery by remember { mutableStateOf("") }
 | 
					 | 
				
			||||||
                            var isMenuExpanded by remember { mutableStateOf(false) }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            val navBackStackEntry by navController.currentBackStackEntryAsState()
 | 
					 | 
				
			||||||
                            val currentRoute = navBackStackEntry?.destination?.route
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            TopAppBar(
 | 
					 | 
				
			||||||
                                title = {
 | 
					 | 
				
			||||||
                                    if(currentRoute == "main") {
 | 
					 | 
				
			||||||
                                        TextField(
 | 
					 | 
				
			||||||
                                            value = searchQuery,
 | 
					 | 
				
			||||||
                                            onValueChange = {
 | 
					 | 
				
			||||||
                                                searchQuery = it
 | 
					 | 
				
			||||||
                                                viewModel.searchQuery.value = it
 | 
					 | 
				
			||||||
                                            },
 | 
					 | 
				
			||||||
                                            placeholder = { Text("Suche...") },
 | 
					 | 
				
			||||||
                                            singleLine = true,
 | 
					 | 
				
			||||||
                                            modifier = Modifier
 | 
					 | 
				
			||||||
                                                .fillMaxWidth()
 | 
					 | 
				
			||||||
                                                .padding(end = 48.dp), // Platz für Avatar
 | 
					 | 
				
			||||||
                                            textStyle = MaterialTheme.typography.bodyLarge,
 | 
					 | 
				
			||||||
                                            colors = TextFieldDefaults.textFieldColors(
 | 
					 | 
				
			||||||
                                                containerColor = Color.Transparent,
 | 
					 | 
				
			||||||
                                                unfocusedIndicatorColor = Color.Transparent,
 | 
					 | 
				
			||||||
                                                focusedIndicatorColor = Color.Transparent
 | 
					 | 
				
			||||||
                                            )
 | 
					 | 
				
			||||||
                                        )
 | 
					 | 
				
			||||||
                                    } else {
 | 
					 | 
				
			||||||
                                        Text("NoteVault")
 | 
					 | 
				
			||||||
                                    }
 | 
					 | 
				
			||||||
                                },
 | 
					 | 
				
			||||||
                                navigationIcon = {
 | 
					 | 
				
			||||||
                                    IconButton(onClick = { scope.launch { drawerState.open() } }) {
 | 
					 | 
				
			||||||
                                        Icon(Icons.Default.Menu, contentDescription = "Menü öffnen")
 | 
					 | 
				
			||||||
                                    }
 | 
					 | 
				
			||||||
                                },
 | 
					 | 
				
			||||||
                                actions = {
 | 
					 | 
				
			||||||
                                    Box {
 | 
					 | 
				
			||||||
                                        IconButton(onClick = { isMenuExpanded = true }) {
 | 
					 | 
				
			||||||
                                            Icon(
 | 
					 | 
				
			||||||
                                                imageVector = Icons.Default.AccountCircle, // Avatar-Icon
 | 
					 | 
				
			||||||
                                                contentDescription = "Benutzerprofil"
 | 
					 | 
				
			||||||
                                            )
 | 
					 | 
				
			||||||
                                        }
 | 
					 | 
				
			||||||
                                        DropdownMenu(
 | 
					 | 
				
			||||||
                                            expanded = isMenuExpanded,
 | 
					 | 
				
			||||||
                                            onDismissRequest = { isMenuExpanded = false }
 | 
					 | 
				
			||||||
                                        ) {
 | 
					 | 
				
			||||||
                                            DropdownMenuItem(
 | 
					 | 
				
			||||||
                                                text = { Text("Profil") },
 | 
					 | 
				
			||||||
                                                onClick = {
 | 
					 | 
				
			||||||
                                                    isMenuExpanded = false
 | 
					 | 
				
			||||||
                                                    // TODO: Navigiere ggf. zu Profilscreen
 | 
					 | 
				
			||||||
                                                }
 | 
					 | 
				
			||||||
                                            )
 | 
					 | 
				
			||||||
                                            DropdownMenuItem(
 | 
					 | 
				
			||||||
                                                text = { Text("Abmelden") },
 | 
					 | 
				
			||||||
                                                onClick = {
 | 
					 | 
				
			||||||
                                                    isMenuExpanded = false
 | 
					 | 
				
			||||||
                                                    // TODO: Logout-Logik
 | 
					 | 
				
			||||||
                                                }
 | 
					 | 
				
			||||||
                                            )
 | 
					 | 
				
			||||||
                                        }
 | 
					 | 
				
			||||||
                                    }
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                            )
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    ) { innerPadding ->
 | 
					 | 
				
			||||||
                        NavHost(
 | 
					 | 
				
			||||||
                            navController = navController,
 | 
					 | 
				
			||||||
                            startDestination = "main",
 | 
					 | 
				
			||||||
                            modifier = Modifier.padding(innerPadding)
 | 
					 | 
				
			||||||
                        ) {
 | 
					 | 
				
			||||||
                            composable("main") {
 | 
					 | 
				
			||||||
                MainScreen(
 | 
					                MainScreen(
 | 
				
			||||||
                    viewModel = viewModel,
 | 
					                    viewModel = viewModel,
 | 
				
			||||||
                    onAddNoteClicked = {
 | 
					                    onAddNoteClicked = {
 | 
				
			||||||
                                        imagePickerLauncher.launch(arrayOf("image/*"))
 | 
					                        imagePickerLauncher.launch(
 | 
				
			||||||
                                    },
 | 
					                            arrayOf("image/*")
 | 
				
			||||||
                                    onEditNote = openDialog
 | 
					 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                            composable("settings") {
 | 
					                )
 | 
				
			||||||
                                SettingsScreen()
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                            composable("notes") {
 | 
					 | 
				
			||||||
                                NotesScreen(collectionRepository, noteRepository, onImportNotes = {
 | 
					 | 
				
			||||||
                                    imagePickerLauncher.launch(arrayOf("image/*"))
 | 
					 | 
				
			||||||
                                }, noteBrowserViewModel,
 | 
					 | 
				
			||||||
                                    onEditNote = {
 | 
					 | 
				
			||||||
                                        noteToEdit = it
 | 
					 | 
				
			||||||
                                        showDialog = true
 | 
					 | 
				
			||||||
                                    })
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Dialog bei Bedarf
 | 
				
			||||||
                if (showDialog) {
 | 
					                if (showDialog) {
 | 
				
			||||||
                            val context = LocalContext.current
 | 
					                    val context = LocalContext.current;
 | 
				
			||||||
 | 
					                    val scope = rememberCoroutineScope();
 | 
				
			||||||
                    AddNoteDialog(
 | 
					                    AddNoteDialog(
 | 
				
			||||||
                        onDismiss = { showDialog = false },
 | 
					                        onDismiss = { showDialog = false },
 | 
				
			||||||
                        onSave = { title, composer, year, genre, description ->
 | 
					                        onSave = { title, composer, year, genre, description ->
 | 
				
			||||||
                                    if (noteToEdit == null) {
 | 
					 | 
				
			||||||
                            viewModel.addNote(
 | 
					                            viewModel.addNote(
 | 
				
			||||||
                                context = context,
 | 
					                                context = context,
 | 
				
			||||||
                                title = title,
 | 
					                                title = title,
 | 
				
			||||||
@ -235,31 +80,12 @@ class MainActivity : ComponentActivity() {
 | 
				
			|||||||
                                genre = genre,
 | 
					                                genre = genre,
 | 
				
			||||||
                                description = description,
 | 
					                                description = description,
 | 
				
			||||||
                                selectedUris = selectedUris,
 | 
					                                selectedUris = selectedUris,
 | 
				
			||||||
                                            collectionID = noteBrowserViewModel.currentParentId.value,
 | 
					 | 
				
			||||||
                                            onDone = { showDialog = false }
 | 
					 | 
				
			||||||
                                        )
 | 
					 | 
				
			||||||
                                    } else {
 | 
					 | 
				
			||||||
                                        viewModel.updateNote(
 | 
					 | 
				
			||||||
                                            editedNote = noteToEdit!!,
 | 
					 | 
				
			||||||
                                            updatedTitle = title,
 | 
					 | 
				
			||||||
                                            updatedComposer = composer,
 | 
					 | 
				
			||||||
                                            updatedYear = year,
 | 
					 | 
				
			||||||
                                            updatedGenre = genre,
 | 
					 | 
				
			||||||
                                            updatedDescription = description,
 | 
					 | 
				
			||||||
                                onDone = { showDialog = false }
 | 
					                                onDone = { showDialog = false }
 | 
				
			||||||
                            )
 | 
					                            )
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                                },
 | 
					 | 
				
			||||||
                                initialTitle = noteToEdit?.title ?: "",
 | 
					 | 
				
			||||||
                                initialComposer = noteToEdit?.composer,
 | 
					 | 
				
			||||||
                                initialYear = noteToEdit?.year,
 | 
					 | 
				
			||||||
                                initialGenre = noteToEdit?.genre,
 | 
					 | 
				
			||||||
                                initialDescription = noteToEdit?.description
 | 
					 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,16 +6,13 @@ 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.NoteEntity
 | 
					import come.stormborntales.notevault.data.local.entity.NoteEntity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Database(entities = [NoteEntity::class, NoteCollection::class], version = 1)
 | 
					@Database(entities = [NoteEntity::class], version = 1)
 | 
				
			||||||
@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
 | 
				
			||||||
 | 
				
			|||||||
@ -1,32 +0,0 @@
 | 
				
			|||||||
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>>
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -5,7 +5,6 @@ import androidx.room.Delete
 | 
				
			|||||||
import androidx.room.Insert
 | 
					import androidx.room.Insert
 | 
				
			||||||
import androidx.room.OnConflictStrategy
 | 
					import androidx.room.OnConflictStrategy
 | 
				
			||||||
import androidx.room.Query
 | 
					import androidx.room.Query
 | 
				
			||||||
import androidx.room.Update
 | 
					 | 
				
			||||||
import come.stormborntales.notevault.data.local.entity.NoteEntity
 | 
					import come.stormborntales.notevault.data.local.entity.NoteEntity
 | 
				
			||||||
import kotlinx.coroutines.flow.Flow
 | 
					import kotlinx.coroutines.flow.Flow
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -19,9 +18,4 @@ interface NoteDao {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Delete
 | 
					    @Delete
 | 
				
			||||||
    suspend fun delete(note: NoteEntity)
 | 
					    suspend fun delete(note: NoteEntity)
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Update
 | 
					 | 
				
			||||||
    suspend fun update(note: NoteEntity)
 | 
					 | 
				
			||||||
    @Query("SELECT * FROM notes WHERE collectionId IS :collectionId")
 | 
					 | 
				
			||||||
    fun getNotesForCollection(collectionId: Int?): Flow<List<NoteEntity>>
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1,11 +0,0 @@
 | 
				
			|||||||
package come.stormborntales.notevault.data.local.entity
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import androidx.room.Entity
 | 
					 | 
				
			||||||
import androidx.room.PrimaryKey
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Entity("note_collections")
 | 
					 | 
				
			||||||
data class NoteCollection(
 | 
					 | 
				
			||||||
    @PrimaryKey(autoGenerate = true) var id: Int = 0,
 | 
					 | 
				
			||||||
    var name: String,
 | 
					 | 
				
			||||||
    var parentId: Int? = null
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
@ -1,24 +1,16 @@
 | 
				
			|||||||
package come.stormborntales.notevault.data.local.entity
 | 
					package come.stormborntales.notevault.data.local.entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import androidx.room.Entity
 | 
					import androidx.room.Entity
 | 
				
			||||||
import androidx.room.ForeignKey
 | 
					 | 
				
			||||||
import androidx.room.PrimaryKey
 | 
					import androidx.room.PrimaryKey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Entity(tableName = "notes",
 | 
					@Entity(tableName = "notes")
 | 
				
			||||||
    foreignKeys = [ForeignKey(
 | 
					 | 
				
			||||||
        entity = NoteCollection::class,
 | 
					 | 
				
			||||||
        parentColumns = ["id"],
 | 
					 | 
				
			||||||
        childColumns = ["collectionId"],
 | 
					 | 
				
			||||||
        onDelete = ForeignKey.CASCADE
 | 
					 | 
				
			||||||
    )])
 | 
					 | 
				
			||||||
data class NoteEntity(
 | 
					data class NoteEntity(
 | 
				
			||||||
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
 | 
					    @PrimaryKey(autoGenerate = true) val id: Int = 0,
 | 
				
			||||||
    var title: String,
 | 
					    val title: String,
 | 
				
			||||||
    val images: List<String>, // oder String + TypeConverter
 | 
					    val images: List<String>, // oder String + TypeConverter
 | 
				
			||||||
    var composer: String?,
 | 
					    val composer: String?,
 | 
				
			||||||
    var year: Int?,
 | 
					    val year: Int?,
 | 
				
			||||||
    var genre: String?,
 | 
					    val genre: String?,
 | 
				
			||||||
    var description: String?,
 | 
					    val description: String?,
 | 
				
			||||||
    val imagePreview: String,
 | 
					    val imagePreview: String
 | 
				
			||||||
    val collectionId: Int?
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -1,34 +0,0 @@
 | 
				
			|||||||
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()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -9,7 +9,4 @@ class NoteRepository(private val dao: NoteDao) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    suspend fun insert(note: NoteEntity) = dao.insert(note)
 | 
					    suspend fun insert(note: NoteEntity) = dao.insert(note)
 | 
				
			||||||
    suspend fun delete(note: NoteEntity) = dao.delete(note)
 | 
					    suspend fun delete(note: NoteEntity) = dao.delete(note)
 | 
				
			||||||
 | 
					 | 
				
			||||||
    suspend fun update(note: NoteEntity) = dao.update(note);
 | 
					 | 
				
			||||||
    fun getNotesForCollection(parentId: Int?) = dao.getNotesForCollection(parentId);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -14,22 +14,15 @@ import androidx.compose.ui.unit.dp
 | 
				
			|||||||
@Composable
 | 
					@Composable
 | 
				
			||||||
fun AddNoteDialog(
 | 
					fun AddNoteDialog(
 | 
				
			||||||
    onDismiss: () -> Unit,
 | 
					    onDismiss: () -> Unit,
 | 
				
			||||||
    onSave: (title: String, composer: String?, year: Int?, genre: String?, description: String?) -> Unit,
 | 
					    onSave: (title: String, composer: String?, year: Int?, genre: String?, description: String?) -> Unit
 | 
				
			||||||
    initialTitle: String = "",
 | 
					 | 
				
			||||||
    initialComposer: String? = null,
 | 
					 | 
				
			||||||
    initialYear: Int? = null,
 | 
					 | 
				
			||||||
    initialGenre: String? = null,
 | 
					 | 
				
			||||||
    initialDescription: String? = null,
 | 
					 | 
				
			||||||
    collectionId: Int? = null
 | 
					 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    var title by remember { mutableStateOf(initialTitle) }
 | 
					    var title by remember { mutableStateOf("") }
 | 
				
			||||||
    var composer by remember { mutableStateOf(initialComposer ?: "") }
 | 
					    var composer by remember { mutableStateOf("") }
 | 
				
			||||||
    var yearText by remember { mutableStateOf(initialYear?.toString() ?: "") }
 | 
					    var yearText by remember { mutableStateOf("") }
 | 
				
			||||||
    var genre by remember { mutableStateOf(initialGenre ?: "") }
 | 
					    var genre by remember { mutableStateOf("") }
 | 
				
			||||||
    var description by remember { mutableStateOf(initialDescription ?: "") }
 | 
					    var description by remember { mutableStateOf("") }
 | 
				
			||||||
    var showTitleError by remember { mutableStateOf(false) }
 | 
					    var showTitleError by remember { mutableStateOf(false) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    AlertDialog(
 | 
					    AlertDialog(
 | 
				
			||||||
        onDismissRequest = onDismiss,
 | 
					        onDismissRequest = onDismiss,
 | 
				
			||||||
        confirmButton = {
 | 
					        confirmButton = {
 | 
				
			||||||
 | 
				
			|||||||
@ -6,13 +6,10 @@ import android.graphics.BitmapFactory
 | 
				
			|||||||
import android.graphics.ImageDecoder
 | 
					import android.graphics.ImageDecoder
 | 
				
			||||||
import android.media.Image
 | 
					import android.media.Image
 | 
				
			||||||
import android.net.Uri
 | 
					import android.net.Uri
 | 
				
			||||||
import android.util.Log
 | 
					 | 
				
			||||||
import androidx.compose.foundation.Image
 | 
					import androidx.compose.foundation.Image
 | 
				
			||||||
import androidx.compose.foundation.horizontalScroll
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.*
 | 
					import androidx.compose.foundation.layout.*
 | 
				
			||||||
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.foundation.rememberScrollState
 | 
					 | 
				
			||||||
import androidx.compose.foundation.shape.RoundedCornerShape
 | 
					import androidx.compose.foundation.shape.RoundedCornerShape
 | 
				
			||||||
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
 | 
				
			||||||
@ -24,7 +21,6 @@ import androidx.compose.ui.Modifier
 | 
				
			|||||||
import androidx.compose.ui.draw.clip
 | 
					import androidx.compose.ui.draw.clip
 | 
				
			||||||
import androidx.compose.ui.graphics.ImageBitmap
 | 
					import androidx.compose.ui.graphics.ImageBitmap
 | 
				
			||||||
import androidx.compose.ui.graphics.asImageBitmap
 | 
					import androidx.compose.ui.graphics.asImageBitmap
 | 
				
			||||||
import androidx.compose.ui.platform.LocalConfiguration
 | 
					 | 
				
			||||||
import androidx.compose.ui.platform.LocalContext
 | 
					import androidx.compose.ui.platform.LocalContext
 | 
				
			||||||
import androidx.compose.ui.unit.dp
 | 
					import androidx.compose.ui.unit.dp
 | 
				
			||||||
import come.stormborntales.notevault.FullscreenImageViewerActivity
 | 
					import come.stormborntales.notevault.FullscreenImageViewerActivity
 | 
				
			||||||
@ -33,7 +29,6 @@ import come.stormborntales.notevault.data.model.NoteEntry
 | 
				
			|||||||
import come.stormborntales.notevault.ui.viewmodel.NoteViewModel
 | 
					import come.stormborntales.notevault.ui.viewmodel.NoteViewModel
 | 
				
			||||||
import java.io.InputStream
 | 
					import java.io.InputStream
 | 
				
			||||||
import androidx.core.net.toUri
 | 
					import androidx.core.net.toUri
 | 
				
			||||||
import androidx.lifecycle.viewmodel.compose.viewModel
 | 
					 | 
				
			||||||
import coil.compose.AsyncImage
 | 
					import coil.compose.AsyncImage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun loadImageBitmap(context: Context, uriString: String): ImageBitmap? {
 | 
					fun loadImageBitmap(context: Context, uriString: String): ImageBitmap? {
 | 
				
			||||||
@ -46,14 +41,12 @@ fun loadImageBitmap(context: Context, uriString: String): ImageBitmap? {
 | 
				
			|||||||
        e.printStackTrace()
 | 
					        e.printStackTrace()
 | 
				
			||||||
        null
 | 
					        null
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}@Composable
 | 
					}
 | 
				
			||||||
fun NoteCard(note: NoteEntity, onDeleteNote: (NoteEntity) -> Unit, onEditNote: (NoteEntity) -> Unit) {
 | 
					
 | 
				
			||||||
 | 
					@Composable
 | 
				
			||||||
 | 
					fun NoteCard(note: NoteEntity) {
 | 
				
			||||||
    val context = LocalContext.current
 | 
					    val context = LocalContext.current
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val screenWidth = LocalConfiguration.current.screenWidthDp // Bildschirmbreite in dp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Dynamische Bildgröße basierend auf der Bildschirmbreite
 | 
					 | 
				
			||||||
    val imageSize = if (screenWidth < 400) 80.dp else 120.dp
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Card(
 | 
					    Card(
 | 
				
			||||||
        modifier = Modifier
 | 
					        modifier = Modifier
 | 
				
			||||||
@ -62,105 +55,22 @@ fun NoteCard(note: NoteEntity, onDeleteNote: (NoteEntity) -> Unit, onEditNote: (
 | 
				
			|||||||
        shape = RoundedCornerShape(16.dp),
 | 
					        shape = RoundedCornerShape(16.dp),
 | 
				
			||||||
        elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
 | 
					        elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        // Responsive Layout: Überprüfen, ob der Bildschirm schmaler als 360 dp ist
 | 
					 | 
				
			||||||
        if (screenWidth < 400) {
 | 
					 | 
				
			||||||
            // Wenn der Bildschirm schmal ist, arrangiere die Elemente vertikal
 | 
					 | 
				
			||||||
            Row(modifier = Modifier.padding(16.dp)) {
 | 
					 | 
				
			||||||
                AsyncImage(
 | 
					 | 
				
			||||||
                    model = note.imagePreview,
 | 
					 | 
				
			||||||
                    contentDescription = "Vorschaubild",
 | 
					 | 
				
			||||||
                    modifier = Modifier
 | 
					 | 
				
			||||||
                        .size(imageSize)
 | 
					 | 
				
			||||||
                        .clip(RoundedCornerShape(12.dp))
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                Column(modifier = Modifier.padding(16.dp)) {
 | 
					 | 
				
			||||||
                    Text(
 | 
					 | 
				
			||||||
                        text = note.title,
 | 
					 | 
				
			||||||
                        style = MaterialTheme.typography.titleMedium
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    note.composer?.let {
 | 
					 | 
				
			||||||
                        Text("von $it", style = MaterialTheme.typography.labelMedium)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    Spacer(modifier = Modifier.height(4.dp))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    note.year?.let {
 | 
					 | 
				
			||||||
                        Text("Jahr: $it", style = MaterialTheme.typography.bodySmall)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    note.genre?.let {
 | 
					 | 
				
			||||||
                        Text("Genre: $it", style = MaterialTheme.typography.bodySmall)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    note.description?.let {
 | 
					 | 
				
			||||||
                        Text("Beschreibung: $it", style = MaterialTheme.typography.bodySmall, maxLines = 2)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    Spacer(modifier = Modifier.height(16.dp)) // Abstand zwischen Text und Buttons
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            // Buttons unter dem Text
 | 
					 | 
				
			||||||
            Row(
 | 
					 | 
				
			||||||
                horizontalArrangement = Arrangement.spacedBy(8.dp),
 | 
					 | 
				
			||||||
                modifier = Modifier.fillMaxWidth().padding(16.dp),
 | 
					 | 
				
			||||||
            ) {
 | 
					 | 
				
			||||||
                OutlinedButton(
 | 
					 | 
				
			||||||
                    onClick = {
 | 
					 | 
				
			||||||
                        val uris = ArrayList<Uri>()
 | 
					 | 
				
			||||||
                        note.images.forEach { uris.add(it.toUri()) }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        val intent = Intent(context, FullscreenImageViewerActivity::class.java).apply {
 | 
					 | 
				
			||||||
                            putParcelableArrayListExtra("imageUris", ArrayList(uris))
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        context.startActivity(intent)
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                    contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
 | 
					 | 
				
			||||||
                ) {
 | 
					 | 
				
			||||||
                    Text("Anzeigen", style = MaterialTheme.typography.labelLarge)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                OutlinedButton(
 | 
					 | 
				
			||||||
                    onClick = {
 | 
					 | 
				
			||||||
                        onEditNote(note)
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                    contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
 | 
					 | 
				
			||||||
                ) {
 | 
					 | 
				
			||||||
                    Text("Bearbeiten", style = MaterialTheme.typography.labelLarge)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                OutlinedButton(
 | 
					 | 
				
			||||||
                    onClick = {
 | 
					 | 
				
			||||||
                        onDeleteNote(note)
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                    colors = ButtonDefaults.outlinedButtonColors(
 | 
					 | 
				
			||||||
                        contentColor = MaterialTheme.colorScheme.error
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                    contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
 | 
					 | 
				
			||||||
                ) {
 | 
					 | 
				
			||||||
                    Text("Löschen", style = MaterialTheme.typography.labelLarge)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            // Wenn der Bildschirm breiter als 360 dp ist, arrangiere die Elemente nebeneinander
 | 
					 | 
				
			||||||
        Row(modifier = Modifier.padding(16.dp)) {
 | 
					        Row(modifier = Modifier.padding(16.dp)) {
 | 
				
			||||||
            // Linkes Vorschaubild
 | 
					            // Linkes Vorschaubild
 | 
				
			||||||
            AsyncImage(
 | 
					            AsyncImage(
 | 
				
			||||||
                model = note.imagePreview,
 | 
					                model = note.imagePreview,
 | 
				
			||||||
                contentDescription = "Vorschaubild",
 | 
					                contentDescription = "Vorschaubild",
 | 
				
			||||||
                modifier = Modifier
 | 
					                modifier = Modifier
 | 
				
			||||||
                        .size(imageSize)
 | 
					                    .size(120.dp)
 | 
				
			||||||
                    .clip(RoundedCornerShape(12.dp))
 | 
					                    .clip(RoundedCornerShape(12.dp))
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Rechte Info-Spalte
 | 
					            // Rechte Info-Spalte
 | 
				
			||||||
            Column(
 | 
					            Column(
 | 
				
			||||||
                modifier = Modifier
 | 
					                modifier = Modifier
 | 
				
			||||||
                        .weight(1f) // Damit die Spalte den verfügbaren Platz nutzt
 | 
					                    .weight(1f)
 | 
				
			||||||
                    .align(Alignment.CenterVertically)
 | 
					                    .align(Alignment.CenterVertically)
 | 
				
			||||||
                        .padding(start = 16.dp)
 | 
					                    .padding(start=16.dp)
 | 
				
			||||||
            ) {
 | 
					            ) {
 | 
				
			||||||
                Text(
 | 
					                Text(
 | 
				
			||||||
                    text = note.title,
 | 
					                    text = note.title,
 | 
				
			||||||
@ -187,14 +97,12 @@ fun NoteCard(note: NoteEntity, onDeleteNote: (NoteEntity) -> Unit, onEditNote: (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                Spacer(modifier = Modifier.height(8.dp))
 | 
					                Spacer(modifier = Modifier.height(8.dp))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // Buttons in einer Row anordnen
 | 
					 | 
				
			||||||
                Row(
 | 
					                Row(
 | 
				
			||||||
                        horizontalArrangement = Arrangement.spacedBy(8.dp),
 | 
					                    horizontalArrangement = Arrangement.spacedBy(8.dp)
 | 
				
			||||||
                        modifier = Modifier.fillMaxWidth() // Stellt sicher, dass die Row den gesamten verfügbaren Platz einnimmt
 | 
					 | 
				
			||||||
                ) {
 | 
					                ) {
 | 
				
			||||||
                    OutlinedButton(
 | 
					                    OutlinedButton(
 | 
				
			||||||
                        onClick = {
 | 
					                        onClick = {
 | 
				
			||||||
                                val uris = ArrayList<Uri>()
 | 
					                            val uris = ArrayList<Uri>();
 | 
				
			||||||
                            note.images.forEach { uris.add(it.toUri()) }
 | 
					                            note.images.forEach { uris.add(it.toUri()) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            val intent = Intent(context, FullscreenImageViewerActivity::class.java).apply {
 | 
					                            val intent = Intent(context, FullscreenImageViewerActivity::class.java).apply {
 | 
				
			||||||
@ -206,20 +114,14 @@ fun NoteCard(note: NoteEntity, onDeleteNote: (NoteEntity) -> Unit, onEditNote: (
 | 
				
			|||||||
                    ) {
 | 
					                    ) {
 | 
				
			||||||
                        Text("Anzeigen", style = MaterialTheme.typography.labelLarge)
 | 
					                        Text("Anzeigen", style = MaterialTheme.typography.labelLarge)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
                    OutlinedButton(
 | 
					                    OutlinedButton(
 | 
				
			||||||
                            onClick = {
 | 
					                        onClick = { /* TODO */ },
 | 
				
			||||||
                                onEditNote(note)
 | 
					 | 
				
			||||||
                            },
 | 
					 | 
				
			||||||
                        contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
 | 
					                        contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
 | 
				
			||||||
                    ) {
 | 
					                    ) {
 | 
				
			||||||
                        Text("Bearbeiten", style = MaterialTheme.typography.labelLarge)
 | 
					                        Text("Bearbeiten", style = MaterialTheme.typography.labelLarge)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
                    OutlinedButton(
 | 
					                    OutlinedButton(
 | 
				
			||||||
                            onClick = {
 | 
					                        onClick = { /* TODO */ },
 | 
				
			||||||
                                onDeleteNote(note)
 | 
					 | 
				
			||||||
                            },
 | 
					 | 
				
			||||||
                        colors = ButtonDefaults.outlinedButtonColors(
 | 
					                        colors = ButtonDefaults.outlinedButtonColors(
 | 
				
			||||||
                            contentColor = MaterialTheme.colorScheme.error
 | 
					                            contentColor = MaterialTheme.colorScheme.error
 | 
				
			||||||
                        ),
 | 
					                        ),
 | 
				
			||||||
@ -231,7 +133,6 @@ fun NoteCard(note: NoteEntity, onDeleteNote: (NoteEntity) -> Unit, onEditNote: (
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -239,11 +140,15 @@ fun NoteCard(note: NoteEntity, onDeleteNote: (NoteEntity) -> Unit, onEditNote: (
 | 
				
			|||||||
@Composable
 | 
					@Composable
 | 
				
			||||||
fun MainScreen(
 | 
					fun MainScreen(
 | 
				
			||||||
    onAddNoteClicked: () -> Unit, // Übergib hier deine Logik für den Import
 | 
					    onAddNoteClicked: () -> Unit, // Übergib hier deine Logik für den Import
 | 
				
			||||||
    viewModel: NoteViewModel,
 | 
					    viewModel: NoteViewModel
 | 
				
			||||||
    onEditNote: (NoteEntity) -> Unit
 | 
					 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    val notes by viewModel.filteredNotes.collectAsState(initial = emptyList())
 | 
					    val notes by viewModel.notes.observeAsState(emptyList())
 | 
				
			||||||
    Scaffold(
 | 
					    Scaffold(
 | 
				
			||||||
 | 
					        topBar = {
 | 
				
			||||||
 | 
					            TopAppBar(
 | 
				
			||||||
 | 
					                title = { Text("Meine Noten") }
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        floatingActionButton = {
 | 
					        floatingActionButton = {
 | 
				
			||||||
            FloatingActionButton(
 | 
					            FloatingActionButton(
 | 
				
			||||||
                onClick = onAddNoteClicked
 | 
					                onClick = onAddNoteClicked
 | 
				
			||||||
@ -260,9 +165,7 @@ fun MainScreen(
 | 
				
			|||||||
                .padding(16.dp)
 | 
					                .padding(16.dp)
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            items(notes) { note ->
 | 
					            items(notes) { note ->
 | 
				
			||||||
                NoteCard(note = note, onDeleteNote = { noteEntity ->
 | 
					                NoteCard(note = note)
 | 
				
			||||||
                    viewModel.deleteNote(noteEntity)
 | 
					 | 
				
			||||||
                }, onEditNote = onEditNote)
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,79 +0,0 @@
 | 
				
			|||||||
package come.stormborntales.notevault.ui.screens
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.Column
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.Row
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.Spacer
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.fillMaxSize
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.fillMaxWidth
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.height
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.padding
 | 
					 | 
				
			||||||
import androidx.compose.material3.Divider
 | 
					 | 
				
			||||||
import androidx.compose.material3.MaterialTheme
 | 
					 | 
				
			||||||
import androidx.compose.material3.OutlinedButton
 | 
					 | 
				
			||||||
import androidx.compose.material3.Switch
 | 
					 | 
				
			||||||
import androidx.compose.material3.Text
 | 
					 | 
				
			||||||
import androidx.compose.runtime.Composable
 | 
					 | 
				
			||||||
import androidx.compose.ui.Alignment
 | 
					 | 
				
			||||||
import androidx.compose.ui.Modifier
 | 
					 | 
				
			||||||
import androidx.compose.ui.unit.dp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Composable
 | 
					 | 
				
			||||||
fun SettingsScreen() {
 | 
					 | 
				
			||||||
    Column(
 | 
					 | 
				
			||||||
        modifier = Modifier
 | 
					 | 
				
			||||||
            .fillMaxSize()
 | 
					 | 
				
			||||||
            .padding(16.dp)
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        Text(
 | 
					 | 
				
			||||||
            text = "Einstellungen",
 | 
					 | 
				
			||||||
            style = MaterialTheme.typography.headlineMedium,
 | 
					 | 
				
			||||||
            modifier = Modifier.padding(bottom = 16.dp)
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Divider()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Spacer(modifier = Modifier.height(16.dp))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Beispielhafte Einstellung
 | 
					 | 
				
			||||||
        Row(
 | 
					 | 
				
			||||||
            verticalAlignment = Alignment.CenterVertically,
 | 
					 | 
				
			||||||
            modifier = Modifier.fillMaxWidth()
 | 
					 | 
				
			||||||
        ) {
 | 
					 | 
				
			||||||
            Text(
 | 
					 | 
				
			||||||
                text = "Dark Mode",
 | 
					 | 
				
			||||||
                modifier = Modifier.weight(1f),
 | 
					 | 
				
			||||||
                style = MaterialTheme.typography.bodyLarge
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            Switch(
 | 
					 | 
				
			||||||
                checked = false,
 | 
					 | 
				
			||||||
                onCheckedChange = { /* TODO: Dark Mode aktivieren */ }
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Spacer(modifier = Modifier.height(16.dp))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Row(
 | 
					 | 
				
			||||||
            verticalAlignment = Alignment.CenterVertically,
 | 
					 | 
				
			||||||
            modifier = Modifier.fillMaxWidth()
 | 
					 | 
				
			||||||
        ) {
 | 
					 | 
				
			||||||
            Text(
 | 
					 | 
				
			||||||
                text = "Benachrichtigungen",
 | 
					 | 
				
			||||||
                modifier = Modifier.weight(1f),
 | 
					 | 
				
			||||||
                style = MaterialTheme.typography.bodyLarge
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            Switch(
 | 
					 | 
				
			||||||
                checked = true,
 | 
					 | 
				
			||||||
                onCheckedChange = { /* TODO: Benachrichtigungseinstellungen */ }
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Spacer(modifier = Modifier.height(32.dp))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        OutlinedButton(
 | 
					 | 
				
			||||||
            onClick = { /* TODO: Impressum anzeigen */ },
 | 
					 | 
				
			||||||
            modifier = Modifier.fillMaxWidth()
 | 
					 | 
				
			||||||
        ) {
 | 
					 | 
				
			||||||
            Text("Impressum")
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,82 +0,0 @@
 | 
				
			|||||||
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")
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,150 +0,0 @@
 | 
				
			|||||||
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,
 | 
					 | 
				
			||||||
    onEditCollection: (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
 | 
					 | 
				
			||||||
                            onEditCollection(collection)
 | 
					 | 
				
			||||||
                        },
 | 
					 | 
				
			||||||
                        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)
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,150 +0,0 @@
 | 
				
			|||||||
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)
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,305 +0,0 @@
 | 
				
			|||||||
package come.stormborntales.notevault.ui.screens.notebrowser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.util.Log
 | 
					 | 
				
			||||||
import androidx.activity.compose.BackHandler
 | 
					 | 
				
			||||||
import androidx.compose.foundation.ExperimentalFoundationApi
 | 
					 | 
				
			||||||
import androidx.compose.foundation.background
 | 
					 | 
				
			||||||
import androidx.compose.foundation.clickable
 | 
					 | 
				
			||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.Arrangement
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.Box
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.Column
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.PaddingValues
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.Row
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.Spacer
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.fillMaxSize
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.fillMaxWidth
 | 
					 | 
				
			||||||
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.layout.wrapContentWidth
 | 
					 | 
				
			||||||
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.shape.RoundedCornerShape
 | 
					 | 
				
			||||||
import androidx.compose.material.icons.Icons
 | 
					 | 
				
			||||||
import androidx.compose.material.icons.filled.Add
 | 
					 | 
				
			||||||
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.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.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
 | 
					 | 
				
			||||||
import come.stormborntales.notevault.data.repository.CollectionRepository
 | 
					 | 
				
			||||||
import come.stormborntales.notevault.data.repository.NoteRepository
 | 
					 | 
				
			||||||
import come.stormborntales.notevault.ui.viewmodel.NoteBrowserViewModel
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Composable
 | 
					 | 
				
			||||||
fun CreateCollectionDialog(
 | 
					 | 
				
			||||||
    editedCollection: NoteCollection?,
 | 
					 | 
				
			||||||
    onDismiss: () -> Unit,
 | 
					 | 
				
			||||||
    onCreate: (String) -> Unit
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    var collectionName by remember { mutableStateOf(editedCollection?.name ?: "") }
 | 
					 | 
				
			||||||
    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 CollectionBreadcrumbs(
 | 
					 | 
				
			||||||
    path: List<NoteCollection>,
 | 
					 | 
				
			||||||
    onNavigateTo: (index: Int) -> Unit
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    Row(
 | 
					 | 
				
			||||||
        modifier = Modifier
 | 
					 | 
				
			||||||
            .fillMaxWidth()
 | 
					 | 
				
			||||||
            .padding(8.dp),
 | 
					 | 
				
			||||||
        verticalAlignment = Alignment.CenterVertically
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        BreadcrumbItem(label = "Start", onClick = { onNavigateTo(-1) })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        path.forEachIndexed { index, collection ->
 | 
					 | 
				
			||||||
            Text(" / ", style = MaterialTheme.typography.labelLarge)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            BreadcrumbItem(label = collection.name, onClick = {
 | 
					 | 
				
			||||||
                Log.d("Breadcrumb", "BreadcrumbItem " + collection.name + " was clicked")
 | 
					 | 
				
			||||||
                onNavigateTo(index)
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Composable
 | 
					 | 
				
			||||||
private fun BreadcrumbItem(label: String, onClick: () -> Unit) {
 | 
					 | 
				
			||||||
    Box(
 | 
					 | 
				
			||||||
        modifier = Modifier
 | 
					 | 
				
			||||||
            .padding(horizontal = 4.dp)
 | 
					 | 
				
			||||||
            .clickable(onClick = {
 | 
					 | 
				
			||||||
                Log.d("Individual Breadcrumb", "A simple test " + label)
 | 
					 | 
				
			||||||
                onClick()
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        Text(
 | 
					 | 
				
			||||||
            text = label,
 | 
					 | 
				
			||||||
            style = MaterialTheme.typography.labelLarge,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@OptIn(ExperimentalFoundationApi::class)
 | 
					 | 
				
			||||||
@Composable
 | 
					 | 
				
			||||||
fun NoteGrid(
 | 
					 | 
				
			||||||
    modifier: Modifier = Modifier,
 | 
					 | 
				
			||||||
    collections: List<NoteCollection>,
 | 
					 | 
				
			||||||
    notes: List<NoteEntity>,
 | 
					 | 
				
			||||||
    onCollectionClick: (NoteCollection) -> Unit,
 | 
					 | 
				
			||||||
    onNoteClick: (NoteEntity) -> Unit,
 | 
					 | 
				
			||||||
    onDeleteNote: (NoteEntity) -> Unit,
 | 
					 | 
				
			||||||
    onEditNote: (NoteEntity) ->Unit,
 | 
					 | 
				
			||||||
    onDeleteCollection: (NoteCollection) -> Unit,
 | 
					 | 
				
			||||||
    onEditCollection: (NoteCollection) -> Unit,
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    var showCollectionConfirmationDialog by remember { mutableStateOf(false) }
 | 
					 | 
				
			||||||
    var deletedCollection: NoteCollection? by remember { mutableStateOf(null) }
 | 
					 | 
				
			||||||
    LazyVerticalGrid(
 | 
					 | 
				
			||||||
        modifier = modifier.fillMaxSize(),
 | 
					 | 
				
			||||||
        columns = GridCells.Adaptive(minSize = 240.dp),
 | 
					 | 
				
			||||||
        contentPadding = PaddingValues(8.dp),
 | 
					 | 
				
			||||||
        verticalArrangement = Arrangement.spacedBy(12.dp),
 | 
					 | 
				
			||||||
        horizontalArrangement = Arrangement.spacedBy(12.dp)
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        items(collections) { collection ->
 | 
					 | 
				
			||||||
            CollectionItem(collection, onDeleteCollection = {
 | 
					 | 
				
			||||||
                showCollectionConfirmationDialog = true
 | 
					 | 
				
			||||||
                deletedCollection = collection
 | 
					 | 
				
			||||||
            }, onEditCollection = onEditCollection)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        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(
 | 
					 | 
				
			||||||
    collectionRepository: CollectionRepository,
 | 
					 | 
				
			||||||
    noteRepository: NoteRepository,
 | 
					 | 
				
			||||||
    onImportNotes: (Int?) -> Unit,
 | 
					 | 
				
			||||||
    viewModel: NoteBrowserViewModel,
 | 
					 | 
				
			||||||
    onEditNote: (NoteEntity) -> Unit
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    var menuExpanded by remember { mutableStateOf(false) }
 | 
					 | 
				
			||||||
    var showDialog by remember { mutableStateOf(false) }
 | 
					 | 
				
			||||||
    var collectionToEdit by remember { mutableStateOf<NoteCollection?>(null) }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val collections by viewModel.currentCollections.collectAsState()
 | 
					 | 
				
			||||||
    val notes by viewModel.currentNotes.collectAsState()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Scaffold(
 | 
					 | 
				
			||||||
        floatingActionButton = {
 | 
					 | 
				
			||||||
            Box {
 | 
					 | 
				
			||||||
                FloatingActionButton(onClick = { menuExpanded = true }) {
 | 
					 | 
				
			||||||
                    Icon(Icons.Default.Add, contentDescription = "Open menu")
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                DropdownMenu(
 | 
					 | 
				
			||||||
                    expanded = menuExpanded,
 | 
					 | 
				
			||||||
                    onDismissRequest = { menuExpanded = false },
 | 
					 | 
				
			||||||
                    modifier = Modifier
 | 
					 | 
				
			||||||
                        .wrapContentWidth()
 | 
					 | 
				
			||||||
                ) {
 | 
					 | 
				
			||||||
                    DropdownMenuItem(
 | 
					 | 
				
			||||||
                        text = { Text("Ordner erstellen") },
 | 
					 | 
				
			||||||
                        onClick = {
 | 
					 | 
				
			||||||
                            menuExpanded = false
 | 
					 | 
				
			||||||
                            showDialog = true
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                    DropdownMenuItem(
 | 
					 | 
				
			||||||
                        text = { Text("Noten importieren") },
 | 
					 | 
				
			||||||
                        onClick = {
 | 
					 | 
				
			||||||
                            menuExpanded = false
 | 
					 | 
				
			||||||
                            onImportNotes(viewModel.currentParentId.value)
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        content = { innerPadding ->
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            val path by viewModel.pathStack.collectAsState()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            BackHandler(enabled = path.isNotEmpty()) {
 | 
					 | 
				
			||||||
                viewModel.navigateUp()
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Column(
 | 
					 | 
				
			||||||
                modifier = Modifier
 | 
					 | 
				
			||||||
                    .fillMaxSize()
 | 
					 | 
				
			||||||
                    .padding(innerPadding)
 | 
					 | 
				
			||||||
            ) {
 | 
					 | 
				
			||||||
                // Breadcrumbs sichtbar & klickbar machen
 | 
					 | 
				
			||||||
                CollectionBreadcrumbs(
 | 
					 | 
				
			||||||
                    path = path,
 | 
					 | 
				
			||||||
                    onNavigateTo = { index ->
 | 
					 | 
				
			||||||
                        Log.d("NoteBrowser", "Navigate to: $index")
 | 
					 | 
				
			||||||
                        if (index == -1) viewModel.loadContentForParent(null)
 | 
					 | 
				
			||||||
                        else viewModel.navigateToLevel(index)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                NoteGrid(
 | 
					 | 
				
			||||||
                    collections = collections,
 | 
					 | 
				
			||||||
                    notes = notes,
 | 
					 | 
				
			||||||
                    onCollectionClick = { viewModel.loadContentForParent(it.id, it) },
 | 
					 | 
				
			||||||
                    onNoteClick = { /* TODO: Öffnen oder Bearbeiten */ },
 | 
					 | 
				
			||||||
                    onDeleteNote = {
 | 
					 | 
				
			||||||
                        viewModel.removeNote(noteEntity = it)
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                    onEditNote = onEditNote,
 | 
					 | 
				
			||||||
                    onDeleteCollection = {
 | 
					 | 
				
			||||||
                        viewModel.removeCollection(noteCollection = it)
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                    onEditCollection = {
 | 
					 | 
				
			||||||
                        collectionToEdit = it
 | 
					 | 
				
			||||||
                        showDialog = true
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (showDialog) {
 | 
					 | 
				
			||||||
                CreateCollectionDialog(
 | 
					 | 
				
			||||||
                    onDismiss = { showDialog = false },
 | 
					 | 
				
			||||||
                    onCreate = { name ->
 | 
					 | 
				
			||||||
                       if(collectionToEdit != null) {
 | 
					 | 
				
			||||||
                           viewModel.updateCollection(collectionToEdit!!, name)
 | 
					 | 
				
			||||||
                       } else {
 | 
					 | 
				
			||||||
                           // Handle the new collection name
 | 
					 | 
				
			||||||
                           viewModel.createCollection(name)
 | 
					 | 
				
			||||||
                       }
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                    editedCollection = collectionToEdit
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,103 +0,0 @@
 | 
				
			|||||||
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
 | 
					 | 
				
			||||||
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()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private val _pathStack = MutableStateFlow<List<NoteCollection>>(emptyList())
 | 
					 | 
				
			||||||
    val pathStack: StateFlow<List<NoteCollection>> = _pathStack.asStateFlow()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    init {
 | 
					 | 
				
			||||||
        loadContentForParent(null)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun loadContentForParent(parentId: Int?, selectedCollection: NoteCollection? = null) {
 | 
					 | 
				
			||||||
        _currentParentId.value = parentId
 | 
					 | 
				
			||||||
        viewModelScope.launch {
 | 
					 | 
				
			||||||
            launch {
 | 
					 | 
				
			||||||
                collectionRepository.getCollectionsByParent(parentId).collect {
 | 
					 | 
				
			||||||
                    _currentCollections.value = it
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            launch {
 | 
					 | 
				
			||||||
                noteRepository.getNotesForCollection(parentId).collect {
 | 
					 | 
				
			||||||
                    Log.d("NoteBrowser", "Noten geladen: ${it.size}")
 | 
					 | 
				
			||||||
                    _currentNotes.value = it
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (selectedCollection != null) {
 | 
					 | 
				
			||||||
                _pathStack.value = _pathStack.value + selectedCollection
 | 
					 | 
				
			||||||
            } else if (parentId == null) {
 | 
					 | 
				
			||||||
                _pathStack.value = emptyList()
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun createCollection(name: String) {
 | 
					 | 
				
			||||||
        viewModelScope.launch {
 | 
					 | 
				
			||||||
            val newCollection = NoteCollection(name = name, parentId = _currentParentId.value)
 | 
					 | 
				
			||||||
            collectionRepository.insertCollection(newCollection)
 | 
					 | 
				
			||||||
            loadContentForParent(_currentParentId.value)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun removeNote(noteEntity: NoteEntity) {
 | 
					 | 
				
			||||||
        viewModelScope.launch {
 | 
					 | 
				
			||||||
            noteRepository.delete(noteEntity)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun removeCollection(noteCollection: NoteCollection) {
 | 
					 | 
				
			||||||
        viewModelScope.launch {
 | 
					 | 
				
			||||||
            collectionRepository.deleteCollection(noteCollection)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun navigateToLevel(index: Int) {
 | 
					 | 
				
			||||||
        val newStack = _pathStack.value.take(index + 1)
 | 
					 | 
				
			||||||
        _pathStack.value = newStack
 | 
					 | 
				
			||||||
        val newParent = newStack.lastOrNull()?.id
 | 
					 | 
				
			||||||
        loadContentForParent(newParent)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun navigateUp() {
 | 
					 | 
				
			||||||
        val newStack = _pathStack.value.dropLast(1)
 | 
					 | 
				
			||||||
        _pathStack.value = newStack
 | 
					 | 
				
			||||||
        val newParentId = newStack.lastOrNull()?.id
 | 
					 | 
				
			||||||
        loadContentForParent(newParentId)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun updateCollection(collectionToEdit: NoteCollection, name: String) {
 | 
					 | 
				
			||||||
        if(name.isNotEmpty()) {
 | 
					 | 
				
			||||||
            collectionToEdit.name = name
 | 
					 | 
				
			||||||
            viewModelScope.launch {
 | 
					 | 
				
			||||||
                collectionRepository.updateCollection(collectionToEdit)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,20 +0,0 @@
 | 
				
			|||||||
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")
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -14,35 +14,13 @@ import come.stormborntales.notevault.data.repository.NoteRepository
 | 
				
			|||||||
import kotlinx.coroutines.Dispatchers
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
import kotlinx.coroutines.launch
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
import java.io.File
 | 
					import java.io.File
 | 
				
			||||||
 | 
					import java.io.InputStream
 | 
				
			||||||
import androidx.core.graphics.scale
 | 
					import androidx.core.graphics.scale
 | 
				
			||||||
import kotlinx.coroutines.flow.Flow
 | 
					 | 
				
			||||||
import kotlinx.coroutines.flow.MutableStateFlow
 | 
					 | 
				
			||||||
import kotlinx.coroutines.flow.SharingStarted
 | 
					 | 
				
			||||||
import kotlinx.coroutines.flow.StateFlow
 | 
					 | 
				
			||||||
import kotlinx.coroutines.flow.combine
 | 
					 | 
				
			||||||
import kotlinx.coroutines.flow.stateIn
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NoteViewModel(
 | 
					class NoteViewModel(
 | 
				
			||||||
    private val repository: NoteRepository,
 | 
					    private val repository: NoteRepository,
 | 
				
			||||||
) : ViewModel() {
 | 
					) : ViewModel() {
 | 
				
			||||||
    // Sucheingabe als StateFlow
 | 
					    val notes = repository.allNotes.asLiveData()
 | 
				
			||||||
    val searchQuery = MutableStateFlow("")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Alle Notizen als Flow aus der Datenbank (NICHT blockierend!)
 | 
					 | 
				
			||||||
    private val allNotes: Flow<List<NoteEntity>> = repository.allNotes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Gefilterte Notizen basierend auf Sucheingabe
 | 
					 | 
				
			||||||
    val filteredNotes: StateFlow<List<NoteEntity>> = combine(searchQuery, allNotes) { query, notes ->
 | 
					 | 
				
			||||||
        if (query.isBlank()) {
 | 
					 | 
				
			||||||
            notes
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            notes.filter {
 | 
					 | 
				
			||||||
                it.title.contains(query, ignoreCase = true) ||
 | 
					 | 
				
			||||||
                        it.description?.contains(query, ignoreCase = true) == true
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun createPreviewImage(context: Context, uri: Uri): File? {
 | 
					    fun createPreviewImage(context: Context, uri: Uri): File? {
 | 
				
			||||||
       return try {
 | 
					       return try {
 | 
				
			||||||
@ -61,7 +39,7 @@ class NoteViewModel(
 | 
				
			|||||||
       }
 | 
					       }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun addNote(context: Context, title: String, composer: String?, year: Int?, genre: String?, description: String?, selectedUris: List<Uri>, collectionID: Int?, onDone:() -> Unit) {
 | 
					    fun addNote(context: Context, title: String, composer: String?, year: Int?, genre: String?, description: String?, selectedUris: List<Uri>, onDone:() -> Unit) {
 | 
				
			||||||
        viewModelScope.launch(Dispatchers.IO) {
 | 
					        viewModelScope.launch(Dispatchers.IO) {
 | 
				
			||||||
            val copiedUris = selectedUris.mapNotNull { uri ->
 | 
					            val copiedUris = selectedUris.mapNotNull { uri ->
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
@ -93,8 +71,7 @@ class NoteViewModel(
 | 
				
			|||||||
                    year = year,
 | 
					                    year = year,
 | 
				
			||||||
                    genre = genre,
 | 
					                    genre = genre,
 | 
				
			||||||
                    description = description,
 | 
					                    description = description,
 | 
				
			||||||
                    imagePreview = preview_image_path.toString(),
 | 
					                    imagePreview = preview_image_path.toString()
 | 
				
			||||||
                    collectionId = collectionID
 | 
					 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                repository.insert(note)
 | 
					                repository.insert(note)
 | 
				
			||||||
@ -109,26 +86,4 @@ class NoteViewModel(
 | 
				
			|||||||
            repository.delete(note)
 | 
					            repository.delete(note)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun updateNote(
 | 
					 | 
				
			||||||
        editedNote: NoteEntity,
 | 
					 | 
				
			||||||
        updatedTitle: String,
 | 
					 | 
				
			||||||
        updatedComposer: String?,
 | 
					 | 
				
			||||||
        updatedYear: Int?,
 | 
					 | 
				
			||||||
        updatedGenre: String?,
 | 
					 | 
				
			||||||
        updatedDescription: String?,
 | 
					 | 
				
			||||||
        onDone: () -> Unit
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        viewModelScope.launch {
 | 
					 | 
				
			||||||
            editedNote.title = updatedTitle
 | 
					 | 
				
			||||||
            editedNote.year = updatedYear;
 | 
					 | 
				
			||||||
            editedNote.composer = updatedComposer
 | 
					 | 
				
			||||||
            editedNote.genre = updatedGenre
 | 
					 | 
				
			||||||
            editedNote.description = updatedDescription
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            repository.update(editedNote)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            onDone()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,14 +9,12 @@ espressoCore = "3.6.1"
 | 
				
			|||||||
lifecycleRuntimeKtx = "2.8.7"
 | 
					lifecycleRuntimeKtx = "2.8.7"
 | 
				
			||||||
activityCompose = "1.8.0"
 | 
					activityCompose = "1.8.0"
 | 
				
			||||||
composeBom = "2024.04.01"
 | 
					composeBom = "2024.04.01"
 | 
				
			||||||
materialIconsExtended = "1.7.8"
 | 
					 | 
				
			||||||
navigationCompose = "2.8.9"
 | 
					navigationCompose = "2.8.9"
 | 
				
			||||||
room = "2.7.1"
 | 
					room = "2.7.1"
 | 
				
			||||||
ksp="2.1.21-RC-2.0.0"
 | 
					ksp="2.1.21-RC-2.0.0"
 | 
				
			||||||
compose = "1.6.0" # oder was immer deine aktuelle Compose-Version ist
 | 
					compose = "1.6.0" # oder was immer deine aktuelle Compose-Version ist
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[libraries]
 | 
					[libraries]
 | 
				
			||||||
androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "materialIconsExtended" }
 | 
					 | 
				
			||||||
coil-compose = { group = "io.coil-kt", name = "coil-compose", version = "2.5.0" } # oder aktueller
 | 
					coil-compose = { group = "io.coil-kt", name = "coil-compose", version = "2.5.0" } # oder aktueller
 | 
				
			||||||
compose-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "compose" }
 | 
					compose-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "compose" }
 | 
				
			||||||
lifecycle-livedata-ktx = "androidx.lifecycle:lifecycle-livedata-ktx:2.8.7"
 | 
					lifecycle-livedata-ktx = "androidx.lifecycle:lifecycle-livedata-ktx:2.8.7"
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user