Skip to content

Commit 3ebe5cc

Browse files
authored
Merge pull request #7888 from Bnyro/master
refactor: migrate search suggestions data to viewmodel / flows
2 parents 8604f27 + 7b86d6c commit 3ebe5cc

3 files changed

Lines changed: 69 additions & 59 deletions

File tree

app/src/main/java/com/github/libretube/db/dao/SearchHistoryDao.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ import androidx.room.Insert
66
import androidx.room.OnConflictStrategy
77
import androidx.room.Query
88
import com.github.libretube.db.obj.SearchHistoryItem
9+
import kotlinx.coroutines.flow.Flow
910

1011
@Dao
1112
interface SearchHistoryDao {
1213
@Query("SELECT * FROM searchHistoryItem")
1314
suspend fun getAll(): List<SearchHistoryItem>
1415

16+
@Query("SELECT * FROM searchHistoryItem")
17+
fun getAllFlow(): Flow<List<SearchHistoryItem>>
18+
1519
@Insert(onConflict = OnConflictStrategy.REPLACE)
1620
suspend fun insert(searchHistoryItem: SearchHistoryItem)
1721

app/src/main/java/com/github/libretube/ui/fragments/SearchSuggestionsFragment.kt

Lines changed: 31 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,19 @@
11
package com.github.libretube.ui.fragments
22

33
import android.os.Bundle
4-
import android.util.Log
54
import android.view.View
65
import androidx.core.view.isGone
76
import androidx.core.view.isVisible
87
import androidx.fragment.app.Fragment
98
import androidx.fragment.app.activityViewModels
10-
import androidx.lifecycle.distinctUntilChanged
9+
import androidx.lifecycle.Lifecycle
1110
import androidx.lifecycle.lifecycleScope
12-
import androidx.lifecycle.map
11+
import androidx.lifecycle.repeatOnLifecycle
1312
import androidx.navigation.fragment.findNavController
1413
import com.github.libretube.R
15-
import com.github.libretube.api.MediaServiceRepository
1614
import com.github.libretube.constants.IntentData
17-
import com.github.libretube.constants.PreferenceKeys
1815
import com.github.libretube.databinding.FragmentSearchSuggestionsBinding
19-
import com.github.libretube.db.DatabaseHolder.Database
20-
import com.github.libretube.extensions.TAG
2116
import com.github.libretube.extensions.anyChildFocused
22-
import com.github.libretube.helpers.PreferenceHelper
2317
import com.github.libretube.ui.activities.MainActivity
2418
import com.github.libretube.ui.adapters.SearchHistoryAdapter
2519
import com.github.libretube.ui.adapters.SearchSuggestionsAdapter
@@ -65,70 +59,48 @@ class SearchSuggestionsFragment : Fragment(R.layout.fragment_search_suggestions)
6559
_binding = FragmentSearchSuggestionsBinding.bind(view)
6660
super.onViewCreated(view, savedInstanceState)
6761

68-
viewModel.searchQuery
69-
.map { it.isNullOrEmpty() }
70-
.distinctUntilChanged()
71-
.observe(viewLifecycleOwner) { isQueryEmpty ->
72-
if (isQueryEmpty) {
73-
binding.suggestionsRecycler.adapter = historyAdapter
74-
} else if (PreferenceHelper.getBoolean(PreferenceKeys.SEARCH_SUGGESTIONS, true)) {
75-
binding.suggestionsRecycler.adapter = suggestionsAdapter
76-
}
77-
}
78-
79-
// waiting for the query to change
8062
viewModel.searchQuery.observe(viewLifecycleOwner) {
81-
showData(it)
82-
}
63+
val isEmpty = it.isNullOrEmpty()
64+
binding.suggestionsRecycler.adapter = if (isEmpty) historyAdapter else suggestionsAdapter
8365

84-
setOnBackPressed {
85-
if (mainActivity.searchView.anyChildFocused()) mainActivity.searchView.clearFocus()
86-
else findNavController().popBackStack()
66+
toggleHistoryVisibility()
8767
}
88-
}
8968

90-
private fun showData(query: String?) {
91-
// fetch search suggestions if enabled or show the search history
92-
binding.historyEmpty.isGone = true
93-
binding.suggestionsRecycler.isVisible = true
94-
if (query.isNullOrEmpty()) {
95-
showHistory()
96-
} else if (PreferenceHelper.getBoolean(PreferenceKeys.SEARCH_SUGGESTIONS, true)) {
97-
fetchSuggestions(query)
69+
lifecycleScope.launch(Dispatchers.IO) {
70+
repeatOnLifecycle(Lifecycle.State.STARTED) {
71+
viewModel.searchSuggestions.collect { suggestions ->
72+
withContext(Dispatchers.Main) {
73+
suggestionsAdapter.submitList(suggestions.reversed())
74+
toggleHistoryVisibility()
75+
}
76+
}
77+
}
9878
}
99-
}
10079

101-
private fun fetchSuggestions(query: String) {
102-
lifecycleScope.launch {
103-
val response = try {
104-
withContext(Dispatchers.IO) {
105-
MediaServiceRepository.instance.getSuggestions(query)
80+
lifecycleScope.launch(Dispatchers.IO) {
81+
repeatOnLifecycle(Lifecycle.State.STARTED) {
82+
viewModel.searchHistory.collect { historyList ->
83+
withContext(Dispatchers.Main) {
84+
historyAdapter.submitList(historyList.map { it.query })
85+
toggleHistoryVisibility()
86+
}
10687
}
107-
} catch (e: Exception) {
108-
Log.e(TAG(), e.toString())
109-
return@launch
110-
}
111-
// only load the suggestions if the input field didn't get cleared yet
112-
if (!viewModel.searchQuery.value.isNullOrEmpty()) {
113-
suggestionsAdapter.submitList(response.reversed())
11488
}
11589
}
116-
}
11790

118-
private fun showHistory() {
119-
lifecycleScope.launch {
120-
val historyList = withContext(Dispatchers.IO) {
121-
Database.searchHistoryDao().getAll().map { it.query }
122-
}
123-
if (historyList.isNotEmpty()) {
124-
historyAdapter.submitList(historyList)
125-
} else {
126-
binding.suggestionsRecycler.isGone = true
127-
binding.historyEmpty.isVisible = true
128-
}
91+
setOnBackPressed {
92+
if (mainActivity.searchView.anyChildFocused()) mainActivity.searchView.clearFocus()
93+
else findNavController().popBackStack()
12994
}
13095
}
13196

97+
private fun toggleHistoryVisibility() {
98+
val isEmpty = viewModel.searchQuery.value.isNullOrEmpty()
99+
val showHistoryEmpty = isEmpty && historyAdapter.currentList.isEmpty()
100+
binding.historyEmpty.isVisible = showHistoryEmpty
101+
binding.suggestionsRecycler.isGone = showHistoryEmpty
102+
}
103+
132104
override fun onDestroy() {
133105
super.onDestroy()
134106

app/src/main/java/com/github/libretube/ui/models/SearchViewModel.kt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,44 @@
11
package com.github.libretube.ui.models
22

3+
import android.util.Log
34
import androidx.lifecycle.MutableLiveData
45
import androidx.lifecycle.ViewModel
6+
import androidx.lifecycle.asFlow
7+
import com.github.libretube.api.MediaServiceRepository
8+
import com.github.libretube.constants.PreferenceKeys
9+
import com.github.libretube.db.DatabaseHolder
10+
import com.github.libretube.helpers.PreferenceHelper
11+
import kotlinx.coroutines.Dispatchers
12+
import kotlinx.coroutines.ExperimentalCoroutinesApi
13+
import kotlinx.coroutines.flow.mapLatest
14+
import kotlinx.coroutines.withContext
515

616
class SearchViewModel : ViewModel() {
717
val searchQuery = MutableLiveData<String>()
18+
19+
@OptIn(ExperimentalCoroutinesApi::class)
20+
val searchSuggestions = searchQuery.asFlow()
21+
.mapLatest { query ->
22+
if (query == null) return@mapLatest emptyList()
23+
24+
if (PreferenceHelper.getBoolean(
25+
PreferenceKeys.SEARCH_SUGGESTIONS,
26+
true
27+
)
28+
) withContext(Dispatchers.IO) {
29+
try {
30+
MediaServiceRepository.instance.getSuggestions(query)
31+
} catch (e: Exception) {
32+
Log.e("failed to fetch suggestions", e.stackTraceToString())
33+
34+
return@withContext emptyList()
35+
}
36+
}
37+
else emptyList()
38+
}
39+
40+
val searchHistory = DatabaseHolder.Database.searchHistoryDao().getAllFlow()
41+
842
fun setQuery(query: String?) {
943
this.searchQuery.value = query
1044
}

0 commit comments

Comments
 (0)