feat: Extract hardcoded strings to string resources
Extracted several hardcoded strings in the UI layer to string resources for improved localization and maintainability.
This commit is contained in:
@@ -4,7 +4,6 @@ import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -33,11 +32,8 @@ import androidx.compose.material.icons.automirrored.filled.PlaylistAddCheck
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.ExposurePlus1
|
||||
import androidx.compose.material.icons.filled.LooksOne
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.filled.Visibility
|
||||
import androidx.compose.material.icons.filled.VisibilityOff
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
@@ -74,8 +70,6 @@ import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.lxtools.noteshop.ui.notes.NoteDetailScreen
|
||||
import de.lxtools.noteshop.ui.notes.NotesScreen
|
||||
@@ -86,7 +80,6 @@ import de.lxtools.noteshop.ui.shopping.ShoppingListsViewModel
|
||||
import de.lxtools.noteshop.ui.theme.NoteshopTheme
|
||||
import kotlinx.coroutines.launch
|
||||
import android.os.Parcelable
|
||||
import de.lxtools.noteshop.data.Note
|
||||
import de.lxtools.noteshop.ui.notes.NoteInputDialog
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@@ -136,7 +129,9 @@ fun AppShell(
|
||||
|
||||
// Collect state from ViewModel
|
||||
val searchQuery by shoppingListsViewModel.searchQuery.collectAsState()
|
||||
val notesSearchQuery by notesViewModel.searchQuery.collectAsState()
|
||||
val isReorderMode by shoppingListsViewModel.isReorderMode.collectAsState()
|
||||
val isNotesReorderMode by notesViewModel.isReorderMode.collectAsState()
|
||||
val newItemName by shoppingListsViewModel.newItemName.collectAsState()
|
||||
val showCompletedItems by shoppingListsViewModel.showCompletedItems.collectAsState()
|
||||
val shoppingListWithItems by shoppingListsViewModel.getShoppingListWithItemsStream(selectedListId ?: 0)
|
||||
@@ -197,11 +192,12 @@ fun AppShell(
|
||||
)
|
||||
}
|
||||
|
||||
val standardListName = stringResource(R.string.standard_list_name)
|
||||
NavigationDrawerItem(
|
||||
label = { Text(stringResource(id = R.string.load_standard_list)) },
|
||||
selected = false,
|
||||
onClick = {
|
||||
shoppingListsViewModel.createStandardList()
|
||||
shoppingListsViewModel.createStandardList(standardListName)
|
||||
scope.launch { drawerState.close() }
|
||||
}
|
||||
)
|
||||
@@ -221,8 +217,11 @@ fun AppShell(
|
||||
TopAppBar(
|
||||
title = { Text(if (currentScreen == Screen.ShoppingListDetail && isDetailSearchActive) stringResource(R.string.search) else topBarTitle) },
|
||||
navigationIcon = {
|
||||
if (isReorderMode) {
|
||||
IconButton(onClick = { shoppingListsViewModel.disableReorderMode() }) {
|
||||
if (isReorderMode || isNotesReorderMode) {
|
||||
IconButton(onClick = {
|
||||
if (isReorderMode) shoppingListsViewModel.disableReorderMode()
|
||||
if (isNotesReorderMode) notesViewModel.disableReorderMode()
|
||||
}) {
|
||||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back))
|
||||
}
|
||||
} else {
|
||||
@@ -266,7 +265,7 @@ fun AppShell(
|
||||
Icon(Icons.Default.Add, contentDescription = stringResource(R.string.add_shopping_list))
|
||||
}
|
||||
}
|
||||
if (currentScreen == Screen.Notes && !isReorderMode) {
|
||||
if (currentScreen == Screen.Notes && !isNotesReorderMode) {
|
||||
IconButton(onClick = {
|
||||
notesViewModel.resetNoteDetails()
|
||||
showNoteDialog = true
|
||||
@@ -304,17 +303,32 @@ fun AppShell(
|
||||
)
|
||||
)
|
||||
|
||||
if (currentScreen == Screen.ShoppingLists) {
|
||||
OutlinedTextField(
|
||||
value = searchQuery,
|
||||
onValueChange = shoppingListsViewModel::updateSearchQuery,
|
||||
label = { Text(stringResource(R.string.search_list_hint)) },
|
||||
leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
)
|
||||
when (currentScreen) {
|
||||
Screen.ShoppingLists -> {
|
||||
OutlinedTextField(
|
||||
value = searchQuery,
|
||||
onValueChange = shoppingListsViewModel::updateSearchQuery,
|
||||
label = { Text(stringResource(R.string.search_list_hint)) },
|
||||
leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
)
|
||||
}
|
||||
Screen.Notes -> {
|
||||
OutlinedTextField(
|
||||
value = notesSearchQuery,
|
||||
onValueChange = notesViewModel::updateSearchQuery,
|
||||
label = { Text(stringResource(R.string.search_notes_hint)) },
|
||||
leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
},
|
||||
bottomBar = {
|
||||
|
||||
@@ -13,7 +13,6 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
||||
@@ -123,7 +123,7 @@ fun NoteCard(
|
||||
) {
|
||||
if (isReorderMode) {
|
||||
IconButton(modifier = with(scope) { Modifier.draggableHandle() }, onClick = {}) {
|
||||
Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder")
|
||||
Icon(Icons.Rounded.DragHandle, contentDescription = stringResource(R.string.reorder))
|
||||
}
|
||||
}
|
||||
Text(
|
||||
@@ -139,7 +139,7 @@ fun NoteCard(
|
||||
}
|
||||
if (isReorderMode) {
|
||||
IconButton(modifier = with(scope) { Modifier.draggableHandle() }, onClick = {}) {
|
||||
Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder")
|
||||
Icon(Icons.Rounded.DragHandle, contentDescription = stringResource(R.string.reorder))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,15 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class NotesViewModel(private val noteshopRepository: NoteshopRepository) : ViewModel() {
|
||||
|
||||
private val _searchQuery = MutableStateFlow("")
|
||||
val searchQuery: StateFlow<String> = _searchQuery.asStateFlow()
|
||||
|
||||
private val _isReorderMode = MutableStateFlow(false)
|
||||
val isReorderMode: StateFlow<Boolean> = _isReorderMode.asStateFlow()
|
||||
|
||||
@@ -25,13 +28,29 @@ class NotesViewModel(private val noteshopRepository: NoteshopRepository) : ViewM
|
||||
_isReorderMode.value = false
|
||||
}
|
||||
|
||||
val uiState: StateFlow<NotesUiState> = noteshopRepository.getAllNotesStream().map { notes ->
|
||||
NotesUiState(notes.sortedBy { it.displayOrder })
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(TIMEOUT_MILLIS),
|
||||
initialValue = NotesUiState()
|
||||
)
|
||||
fun updateSearchQuery(query: String) {
|
||||
_searchQuery.value = query
|
||||
}
|
||||
|
||||
val uiState: StateFlow<NotesUiState> = combine(
|
||||
noteshopRepository.getAllNotesStream(),
|
||||
searchQuery
|
||||
) { allNotes, query ->
|
||||
val filteredNotes = if (query.isBlank()) {
|
||||
allNotes
|
||||
} else {
|
||||
allNotes.filter {
|
||||
it.title.contains(query, ignoreCase = true) ||
|
||||
it.content.contains(query, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
NotesUiState(filteredNotes.sortedBy { it.displayOrder })
|
||||
}
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(TIMEOUT_MILLIS),
|
||||
initialValue = NotesUiState()
|
||||
)
|
||||
|
||||
private val _noteDetails = MutableStateFlow(NoteDetails())
|
||||
val noteDetails: StateFlow<NoteDetails> = _noteDetails.asStateFlow()
|
||||
@@ -106,4 +125,4 @@ data class NoteDetails(
|
||||
fun isValid(): Boolean {
|
||||
return title.isNotBlank()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,7 +213,7 @@ fun ShoppingListDetailScreen(
|
||||
) {
|
||||
if (isReorderMode) {
|
||||
IconButton(modifier = Modifier.draggableHandle(), onClick = {}) {
|
||||
Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder")
|
||||
Icon(Icons.Rounded.DragHandle, contentDescription = stringResource(R.string.reorder))
|
||||
}
|
||||
}
|
||||
Text(
|
||||
@@ -227,7 +227,7 @@ fun ShoppingListDetailScreen(
|
||||
viewModel.onSelectItem(item)
|
||||
showMenu = true
|
||||
}) {
|
||||
Icon(Icons.Default.MoreVert, contentDescription = "More options")
|
||||
Icon(Icons.Default.MoreVert, contentDescription = stringResource(R.string.more_options))
|
||||
DropdownMenu(expanded = showMenu, onDismissRequest = { showMenu = false }) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.rename_item_title)) },
|
||||
@@ -247,7 +247,7 @@ fun ShoppingListDetailScreen(
|
||||
}
|
||||
if (isReorderMode) {
|
||||
IconButton(modifier = Modifier.draggableHandle(), onClick = {}) {
|
||||
Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder")
|
||||
Icon(Icons.Rounded.DragHandle, contentDescription = stringResource(R.string.reorder))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ fun ShoppingListCard(
|
||||
) {
|
||||
if (isReorderMode) {
|
||||
IconButton(modifier = with(scope) { Modifier.draggableHandle() }, onClick = {}) {
|
||||
Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder")
|
||||
Icon(Icons.Rounded.DragHandle, contentDescription = stringResource(R.string.reorder))
|
||||
}
|
||||
}
|
||||
Text(
|
||||
@@ -147,7 +147,7 @@ fun ShoppingListCard(
|
||||
}
|
||||
if (isReorderMode) {
|
||||
IconButton(modifier = with(scope) { Modifier.draggableHandle() }, onClick = {}) {
|
||||
Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder")
|
||||
Icon(Icons.Rounded.DragHandle, contentDescription = stringResource(R.string.reorder))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,10 +290,9 @@ class ShoppingListsViewModel(private val noteshopRepository: NoteshopRepository)
|
||||
}
|
||||
}
|
||||
|
||||
fun createStandardList() {
|
||||
fun createStandardList(standardListName: String) {
|
||||
viewModelScope.launch {
|
||||
val allLists = uiState.value.shoppingLists
|
||||
val standardListName = "Standard"
|
||||
var newListName = standardListName
|
||||
var counter = 1
|
||||
while (allLists.any { it.shoppingList.name == newListName }) {
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<string name="no_items_in_list">Noch keine Artikel in dieser Liste.</string>
|
||||
<string name="shopping_list_not_found">Einkaufsliste nicht gefunden.</string>
|
||||
<string name="search_list_hint">Listen suchen...</string>
|
||||
<string name="search_notes_hint">Notizen durchsuchen...</string>
|
||||
<string name="add_item_simple_mode">Hinzufügen (Einfach)</string>
|
||||
<string name="add_item_icon_desc">Artikel hinzufügen</string>
|
||||
<string name="add_item_simple_icon_desc">Artikel hinzufügen (einfacher Modus)</string>
|
||||
@@ -60,6 +61,8 @@
|
||||
<string name="already_in_list">ist schon in der liste</string>
|
||||
<string name="load_standard_list">Standardliste laden</string>
|
||||
<string name="search">Suche</string>
|
||||
<string name="reorder">Neu anordnen</string>
|
||||
<string name="standard_list_name">Standard</string>
|
||||
<string-array name="standard_list_items">
|
||||
<item>Brötchen</item>
|
||||
<item>Red Bull</item>
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<string name="no_items_in_list">No items in this list yet.</string>
|
||||
<string name="shopping_list_not_found">Shopping list not found.</string>
|
||||
<string name="search_list_hint">Search lists...</string>
|
||||
<string name="search_notes_hint">Search notes...</string>
|
||||
<string name="add_item_simple_mode">Add (Simple Mode)</string>
|
||||
<string name="add_item_icon_desc">Add item(s)</string>
|
||||
<string name="add_item_simple_icon_desc">Add item (simple mode)</string>
|
||||
@@ -60,6 +61,8 @@
|
||||
<string name="already_in_list">already in list</string>
|
||||
<string name="load_standard_list">Load standard list</string>
|
||||
<string name="search">Search</string>
|
||||
<string name="reorder">Reorder</string>
|
||||
<string name="standard_list_name">Standard</string>
|
||||
<string-array name="standard_list_items">
|
||||
<item>Bread rolls</item>
|
||||
<item>Red Bull</item>
|
||||
|
||||
Reference in New Issue
Block a user