Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed UI State Loss In BrainzPlayerSearchScreen #534

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
Expand All @@ -26,6 +27,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue
import androidx.hilt.navigation.compose.hiltViewModel
import org.listenbrainz.android.R
import org.listenbrainz.android.model.PlayableType
Expand All @@ -46,21 +48,15 @@ fun BrainzPlayerSearchScreen(
deactivate: () -> Unit,
) {
val context = LocalContext.current
var brainzplayerQueryState by remember {
mutableStateOf("")
}

val searchItems = remember {
mutableStateListOf<Song>()
}
val brainzplayerQueryState by viewModel.searchQuery.collectAsState()
val searchItems by viewModel.searchItems.collectAsState()

var error by remember {
mutableStateOf<ResponseError?>(null)
}

fun onDismiss() {
searchItems.clear()
brainzplayerQueryState = ""
viewModel.clearSearchResults()
error = null
deactivate()
}
Expand All @@ -71,20 +67,21 @@ fun BrainzPlayerSearchScreen(
exit = fadeOut()
) {
SearchScreen(
uiState = remember(searchItems, brainzplayerQueryState, error) {
uiState = remember(searchItems, brainzplayerQueryState.text, error) {
SearchUiState(
query = brainzplayerQueryState,
query = brainzplayerQueryState.text,
result = searchItems,
error = error
)
},
onDismiss = ::onDismiss,
onQueryChange = { newValue ->
brainzplayerQueryState = newValue
searchItems.clear()
searchItems.addAll(viewModel.searchSongs(brainzplayerQueryState) ?: emptyList())
onQueryChange = { newValue: String ->
val updatedQuery = TextFieldValue(newValue, selection = brainzplayerQueryState.selection)
viewModel.updateSearchQuery(updatedQuery)
},
onClear = {
viewModel.clearSearchResults()
},
onClear = searchItems::clear,
onErrorShown = { error = null },
placeholderText = "Search your music library"
) {
Expand All @@ -102,12 +99,17 @@ fun BrainzPlayerSearchScreen(
onDropdownSuccess = { context.showToast(it) },
onDropdownError = { error = it }
) {
viewModel.changePlayable(listOf(song), PlayableType.SONG, song.mediaID, 0)
viewModel.changePlayable(
listOf(song),
PlayableType.SONG,
song.mediaID,
0
)
viewModel.playOrToggleSong(song, true)
onDismiss()
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.palette.graphics.Palette
Expand All @@ -23,8 +24,12 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import org.listenbrainz.android.model.Playable
import org.listenbrainz.android.model.PlayableType
Expand Down Expand Up @@ -67,10 +72,15 @@ class BrainzPlayerViewModel @Inject constructor(
val isPlaying = brainzPlayerServiceConnection.isPlaying
val playButton = brainzPlayerServiceConnection.playButtonState
val repeatMode = brainzPlayerServiceConnection.repeatModeState
var isSearching by mutableStateOf(false)

var playerBackGroundColor by mutableStateOf(Color.Transparent)

private val _searchQuery = MutableStateFlow(TextFieldValue(""))
val searchQuery: StateFlow<TextFieldValue> = _searchQuery

private val _searchItems = MutableStateFlow<List<Song>>(emptyList())
val searchItems: StateFlow<List<Song>> = _searchItems

init {
updatePlayerPosition()
_mediaItems.value = Resource.loading()
Expand All @@ -96,6 +106,16 @@ class BrainzPlayerViewModel @Inject constructor(
currentlyPlaying.items.plus(it)
}
}

viewModelScope.launch {
_searchQuery
.map { it.text }
.debounce(200) // 0.2 Seconds
.distinctUntilChanged()
.collect { query ->
SearchSong(query)
}
}
}

fun updateBackgroundColorForPlayer(
Expand Down Expand Up @@ -191,19 +211,28 @@ class BrainzPlayerViewModel @Inject constructor(
}
}

fun searchSongs(query: String): List<Song>? {
val listToSearch = _mediaItems.value.data
fun updateSearchQuery(newQuery: TextFieldValue) {
_searchQuery.value = newQuery
}

private fun SearchSong(query: String) {
val listToSearch = _mediaItems.value.data
if (query.isEmpty()) {
isSearching = false
_searchItems.value = emptyList()
return
}
val result: List<Song>? = listToSearch?.filter {
it.title.contains(query.trim(), ignoreCase = true)
}
isSearching = true
return result
_searchItems.value = result ?: emptyList()
}

fun clearSearchResults() {
_searchItems.value = emptyList()
_searchQuery.value = TextFieldValue("")
}


fun playOrToggleSong(mediaItem: Song, toggle: Boolean = false) {
val isPrepared = playbackState.value.isPrepared
if (isPrepared && mediaItem.mediaID == currentlyPlayingSong.value.toSong.mediaID) {
Expand Down