nextNoteVault #23
@ -4,10 +4,10 @@
|
|||||||
<selectionStates>
|
<selectionStates>
|
||||||
<SelectionState runConfigName="app">
|
<SelectionState runConfigName="app">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
<DropdownSelection timestamp="2025-05-03T08:26:48.052108137Z">
|
<DropdownSelection timestamp="2025-05-03T08:34:29.354537334Z">
|
||||||
<Target type="DEFAULT_BOOT">
|
<Target type="DEFAULT_BOOT">
|
||||||
<handle>
|
<handle>
|
||||||
<DeviceId pluginId="LocalEmulator" identifier="path=/home/sebastian/.android/avd/Small_Phone_API_36.avd" />
|
<DeviceId pluginId="PhysicalDevice" identifier="serial=R52N50NLGRT" />
|
||||||
</handle>
|
</handle>
|
||||||
</Target>
|
</Target>
|
||||||
</DropdownSelection>
|
</DropdownSelection>
|
||||||
|
@ -7,10 +7,15 @@ 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.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.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.AccountCircle
|
||||||
import androidx.compose.material.icons.filled.Menu
|
import androidx.compose.material.icons.filled.Menu
|
||||||
import androidx.compose.material3.DrawerValue
|
import androidx.compose.material3.DrawerValue
|
||||||
|
import androidx.compose.material3.DropdownMenu
|
||||||
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
@ -21,15 +26,19 @@ import androidx.compose.material3.NavigationDrawerItem
|
|||||||
import androidx.compose.material3.NavigationDrawerItemDefaults
|
import androidx.compose.material3.NavigationDrawerItemDefaults
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextField
|
||||||
|
import androidx.compose.material3.TextFieldDefaults
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.material3.rememberDrawerState
|
import androidx.compose.material3.rememberDrawerState
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
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.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.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import come.stormborntales.notevault.data.local.AppDatabase
|
import come.stormborntales.notevault.data.local.AppDatabase
|
||||||
import come.stormborntales.notevault.data.local.entity.NoteEntity
|
import come.stormborntales.notevault.data.local.entity.NoteEntity
|
||||||
@ -109,13 +118,69 @@ class MainActivity : ComponentActivity() {
|
|||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
|
var searchQuery by remember { mutableStateOf("") }
|
||||||
|
var isMenuExpanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||||
|
val currentRoute = navBackStackEntry?.destination?.route
|
||||||
|
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { Text("NoteVault") },
|
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 = {
|
navigationIcon = {
|
||||||
IconButton(onClick = {
|
IconButton(onClick = { scope.launch { drawerState.open() } }) {
|
||||||
scope.launch { drawerState.open() }
|
Icon(Icons.Default.Menu, contentDescription = "Menü öffnen")
|
||||||
}) {
|
}
|
||||||
Icon(Icons.Default.Menu, contentDescription = "Menu")
|
},
|
||||||
|
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
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -179,7 +244,5 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,7 +242,7 @@ fun MainScreen(
|
|||||||
viewModel: NoteViewModel,
|
viewModel: NoteViewModel,
|
||||||
onEditNote: (NoteEntity) -> Unit
|
onEditNote: (NoteEntity) -> Unit
|
||||||
) {
|
) {
|
||||||
val notes by viewModel.notes.observeAsState(emptyList())
|
val notes by viewModel.filteredNotes.collectAsState(initial = emptyList())
|
||||||
Scaffold(
|
Scaffold(
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
|
@ -15,11 +15,34 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.File
|
import java.io.File
|
||||||
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() {
|
||||||
val notes = repository.allNotes.asLiveData()
|
// Sucheingabe als StateFlow
|
||||||
|
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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user