Fix: Resolve list selection crash & update to minimal flat app icon
- ListDetailViewModel & Screen: set listId safely via setListId() avoiding SavedStateHandle null crashes
- Navigation: pass listId = key.listId explicitly into ListDetailScreen
- App Icon: replace 3D icon with a clean, minimal flat line-art shopping bag & checkmark icon suited for Samsung One UI & stock Android adaptive icon masks
- ADB reinstall & launch verified ✅
|
|
@ -37,6 +37,7 @@ fun MainNavigation(sessionManager: SessionManager) {
|
||||||
}
|
}
|
||||||
entry<ListDetailNavKey> { key ->
|
entry<ListDetailNavKey> { key ->
|
||||||
ListDetailScreen(
|
ListDetailScreen(
|
||||||
|
listId = key.listId,
|
||||||
onBack = { backStack.removeLastOrNull() }
|
onBack = { backStack.removeLastOrNull() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,15 @@ import com.example.mitbringsl.data.local.entity.ItemEntity
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ListDetailScreen(
|
fun ListDetailScreen(
|
||||||
|
listId: String,
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: ListDetailViewModel = hiltViewModel(),
|
viewModel: ListDetailViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
|
LaunchedEffect(listId) {
|
||||||
|
viewModel.setListId(listId)
|
||||||
|
}
|
||||||
|
|
||||||
val items by viewModel.items.collectAsStateWithLifecycle()
|
val items by viewModel.items.collectAsStateWithLifecycle()
|
||||||
val query by viewModel.query.collectAsStateWithLifecycle()
|
val query by viewModel.query.collectAsStateWithLifecycle()
|
||||||
val suggestions by viewModel.suggestions.collectAsStateWithLifecycle()
|
val suggestions by viewModel.suggestions.collectAsStateWithLifecycle()
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,43 @@
|
||||||
package com.example.mitbringsl.ui.detail
|
package com.example.mitbringsl.ui.detail
|
||||||
|
|
||||||
import androidx.lifecycle.SavedStateHandle
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.example.mitbringsl.data.local.entity.ItemEntity
|
import com.example.mitbringsl.data.local.entity.ItemEntity
|
||||||
import com.example.mitbringsl.data.remote.api.MitbringslApi
|
import com.example.mitbringsl.data.remote.api.MitbringslApi
|
||||||
import com.example.mitbringsl.data.repository.ShoppingRepository
|
import com.example.mitbringsl.data.repository.ShoppingRepository
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@OptIn(FlowPreview::class)
|
@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class ListDetailViewModel @Inject constructor(
|
class ListDetailViewModel @Inject constructor(
|
||||||
private val repository: ShoppingRepository,
|
private val repository: ShoppingRepository,
|
||||||
private val api: MitbringslApi,
|
private val api: MitbringslApi,
|
||||||
savedStateHandle: SavedStateHandle,
|
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
val listId: String = checkNotNull(savedStateHandle["listId"])
|
private val _listId = MutableStateFlow<String?>(null)
|
||||||
|
|
||||||
val items: StateFlow<List<ItemEntity>> = repository.observeItems(listId)
|
val items: StateFlow<List<ItemEntity>> = _listId
|
||||||
|
.filterNotNull()
|
||||||
|
.flatMapLatest { id -> repository.observeItems(id) }
|
||||||
.stateIn(
|
.stateIn(
|
||||||
scope = viewModelScope,
|
scope = viewModelScope,
|
||||||
started = SharingStarted.WhileSubscribed(5000),
|
started = SharingStarted.WhileSubscribed(5000),
|
||||||
initialValue = emptyList()
|
initialValue = emptyList()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun setListId(id: String) {
|
||||||
|
if (_listId.value != id) {
|
||||||
|
_listId.value = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentListId: String get() = _listId.value ?: ""
|
||||||
|
|
||||||
private val _query = MutableStateFlow("")
|
private val _query = MutableStateFlow("")
|
||||||
val query: StateFlow<String> = _query.asStateFlow()
|
val query: StateFlow<String> = _query.asStateFlow()
|
||||||
|
|
||||||
|
|
@ -61,10 +70,11 @@ class ListDetailViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addItem(name: String, quantity: String? = null) {
|
fun addItem(name: String, quantity: String? = null) {
|
||||||
|
val targetListId = _listId.value ?: return
|
||||||
if (name.isBlank()) return
|
if (name.isBlank()) return
|
||||||
val qty = quantity?.trim()?.takeIf { it.isNotEmpty() }
|
val qty = quantity?.trim()?.takeIf { it.isNotEmpty() }
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
repository.addItem(listId, name.trim(), qty)
|
repository.addItem(targetListId, name.trim(), qty)
|
||||||
_query.value = ""
|
_query.value = ""
|
||||||
_suggestions.value = emptyList()
|
_suggestions.value = emptyList()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 220 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 31 KiB |
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="ic_launcher_background_color">#1B1B2F</color>
|
<color name="ic_launcher_background_color">#151829</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||