From d069952f5ab5a4bf147be84b37f767c20861a9aa Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 6 Sep 2022 09:59:22 -0700 Subject: [PATCH 1/6] Add search bar to android home feed --- .../app/omnivore/omnivore/ui/home/HomeView.kt | 43 +++--- .../omnivore/ui/home/HomeViewModel.kt | 57 +++++--- .../omnivore/ui/home/LinkedItemCard.kt | 2 +- .../omnivore/omnivore/ui/home/SearchBar.kt | 123 ++++++++++++++++++ 4 files changed, 182 insertions(+), 43 deletions(-) create mode 100644 android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/SearchBar.kt diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt index 849615447..fa90f3f0f 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt @@ -28,32 +28,27 @@ fun HomeView( homeViewModel: HomeViewModel, navController: NavHostController ) { - Scaffold( - topBar = { - TopAppBar( - title = { Text("Home") }, - backgroundColor = MaterialTheme.colorScheme.surfaceVariant, - actions = { - IconButton(onClick = { navController.navigate(Routes.Settings.route) }) { - Icon( - imageVector = Icons.Default.Menu, - contentDescription = null - ) - } - } - ) - } - ) { paddingValues -> - HomeViewContent( - homeViewModel, - navController, - modifier = Modifier - .padding( - top = paddingValues.calculateTopPadding(), - bottom = paddingValues.calculateBottomPadding() - ) + val searchText: String by homeViewModel.searchTextLiveData.observeAsState("") + + Scaffold( + topBar = { + SearchBar( + searchText = searchText, + onSearchTextChanged = { homeViewModel.updateSearchText(it) }, + onSettingsIconClick = { navController.navigate(Routes.Settings.route) } ) } + ) { paddingValues -> + HomeViewContent( + homeViewModel, + navController, + modifier = Modifier + .padding( + top = paddingValues.calculateTopPadding(), + bottom = paddingValues.calculateBottomPadding() + ) + ) + } } @Composable diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt index 49c91433e..b2d63e0d2 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt @@ -1,26 +1,12 @@ package app.omnivore.omnivore.ui.home -import android.annotation.SuppressLint -import android.net.Uri -import android.view.ViewGroup -import android.webkit.WebResourceRequest -import android.webkit.WebView -import android.webkit.WebViewClient -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Surface -import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import androidx.compose.ui.viewinterop.AndroidView -import androidx.compose.ui.window.Dialog +import android.util.Log import androidx.core.net.toUri import androidx.lifecycle.* -import app.omnivore.omnivore.AppleConstants import app.omnivore.omnivore.Constants import app.omnivore.omnivore.DatastoreKeys import app.omnivore.omnivore.DatastoreRepository import app.omnivore.omnivore.graphql.generated.SearchQuery -import app.omnivore.omnivore.ui.auth.AppleAuthDialog import com.apollographql.apollo3.ApolloClient import com.apollographql.apollo3.api.Optional import dagger.hilt.android.lifecycle.HiltViewModel @@ -32,14 +18,29 @@ import javax.inject.Inject class HomeViewModel @Inject constructor( private val datastoreRepo: DatastoreRepository ): ViewModel() { - var cursor: String? = null + private var cursor: String? = null + private var items: List = listOf() + private var searchedItems: List = listOf() + + // Live Data + val searchTextLiveData = MutableLiveData("") val itemsLiveData = MutableLiveData>(listOf()) private fun getAuthToken(): String? = runBlocking { datastoreRepo.getString(DatastoreKeys.omnivoreAuthToken) } - fun load() { + fun updateSearchText(text: String) { + searchTextLiveData.value = text + + if (text == "") { + itemsLiveData.value = items + } else { + load(clearPreviousSearch = true) + } + } + + fun load(clearPreviousSearch: Boolean = false) { viewModelScope.launch { val authToken = getAuthToken() @@ -52,6 +53,7 @@ class HomeViewModel @Inject constructor( SearchQuery( after = Optional.presentIfNotNull(cursor), first = Optional.presentIfNotNull(15), + query = Optional.presentIfNotNull(searchQuery()) ) ).execute() @@ -74,9 +76,28 @@ class HomeViewModel @Inject constructor( ) } - itemsLiveData.value = (itemsLiveData.value ?: listOf()).plus(newItems) + Log.d("loggo", newItems.toString()) + + if (searchTextLiveData.value != "") { + val previousItems = if (clearPreviousSearch) listOf() else searchedItems + searchedItems = previousItems.plus(newItems) + itemsLiveData.value = searchedItems + } else { + items = items.plus(newItems) + itemsLiveData.value = items + } } } + + private fun searchQuery(): String { + var query = "in:inbox sort:saved" + + if (searchTextLiveData.value != "") { + query.plus(" ${searchTextLiveData.value}") + } + + return query + } } public data class LinkedItem( diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/LinkedItemCard.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/LinkedItemCard.kt index 71e4d41e4..eb6c42e37 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/LinkedItemCard.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/LinkedItemCard.kt @@ -31,7 +31,7 @@ fun LinkedItemCard(item: LinkedItem, onClickHandler: () -> Unit) { .clickable(onClick = onClickHandler) ) { Column( - verticalArrangement = Arrangement.spacedBy(4.dp), + verticalArrangement = Arrangement.spacedBy(2.dp), modifier = Modifier .weight(1f, fill = false) .padding(end = 8.dp) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/SearchBar.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/SearchBar.kt new file mode 100644 index 000000000..4cd11417c --- /dev/null +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/SearchBar.kt @@ -0,0 +1,123 @@ +package app.omnivore.omnivore.ui.home + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.LocalContentAlpha +import androidx.compose.material.TopAppBar +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.unit.dp + +@Composable +fun SearchBar( + searchText: String, + onSearchTextChanged: (String) -> Unit, + onSettingsIconClick: () -> Unit +) { + var showSearchField by remember { mutableStateOf(false) } + TopAppBar( + title = { + if (showSearchField) { + SearchField(searchText, onSearchTextChanged) + } else { + Text("Home") + } + }, + backgroundColor = MaterialTheme.colorScheme.surfaceVariant, + actions = { + if (showSearchField) { + Text( + text = "Cancel", + modifier = Modifier + .clickable { showSearchField = false } + ) + } else { + IconButton(onClick = { showSearchField = true }) { + Icon( + imageVector = Icons.Filled.Search, + contentDescription = null + ) + } + + IconButton(onClick = onSettingsIconClick) { + Icon( + imageVector = Icons.Filled.Settings, + contentDescription = null + ) + } + } + } + ) +} + + +@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class) +@Composable +fun SearchField( + searchText: String, + onSearchTextChanged: (String) -> Unit +) { + var showClearButton by remember { mutableStateOf(false) } + val keyboardController = LocalSoftwareKeyboardController.current + val focusRequester = remember { FocusRequester() } + + Row { + TextField( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 2.dp) + .onFocusChanged { focusState -> + showClearButton = (focusState.isFocused) + } + .focusRequester(focusRequester), + value = searchText, + onValueChange = onSearchTextChanged, + placeholder = { + Text(text = "Search") + }, + trailingIcon = { + AnimatedVisibility( + visible = showClearButton, + enter = fadeIn(), + exit = fadeOut() + ) { + IconButton(onClick = { onSearchTextChanged("") }) { + Icon( + imageVector = Icons.Filled.Close, + contentDescription = null + ) + } + + } + }, + maxLines = 1, + singleLine = true, + keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions(onDone = { + keyboardController?.hide() + }), + ) + } + + + LaunchedEffect(Unit) { + focusRequester.requestFocus() + } +} From e4e5afe072bc69d9098e8792a16a2dbbe32ea0d3 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 6 Sep 2022 13:10:10 -0700 Subject: [PATCH 2/6] use material3 topappbar --- .../app/omnivore/omnivore/ui/auth/AppleAuth.kt | 1 - .../app/omnivore/omnivore/ui/home/HomeView.kt | 5 ----- .../omnivore/omnivore/ui/home/HomeViewModel.kt | 2 -- .../app/omnivore/omnivore/ui/home/SearchBar.kt | 17 +++++++++-------- .../omnivore/ui/settings/SettingsContent.kt | 7 +------ 5 files changed, 10 insertions(+), 22 deletions(-) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/AppleAuth.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/AppleAuth.kt index 13f9c9626..54c001f11 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/AppleAuth.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/AppleAuth.kt @@ -8,7 +8,6 @@ import android.view.ViewGroup import android.webkit.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.TopAppBar import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt index fa90f3f0f..58a8a0d53 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt @@ -1,21 +1,16 @@ package app.omnivore.omnivore.ui.home -import android.annotation.SuppressLint import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material.TopAppBar -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Menu import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import app.omnivore.omnivore.Routes diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt index b2d63e0d2..fdc4c2671 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt @@ -76,8 +76,6 @@ class HomeViewModel @Inject constructor( ) } - Log.d("loggo", newItems.toString()) - if (searchTextLiveData.value != "") { val previousItems = if (clearPreviousSearch) listOf() else searchedItems searchedItems = previousItems.plus(newItems) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/SearchBar.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/SearchBar.kt index 4cd11417c..cd1b46b59 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/SearchBar.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/SearchBar.kt @@ -9,10 +9,10 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.LocalContentAlpha -import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.* +import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.Search +import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.ExperimentalComposeUiApi @@ -20,19 +20,20 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp +@OptIn(ExperimentalMaterial3Api::class) @Composable fun SearchBar( searchText: String, onSearchTextChanged: (String) -> Unit, onSettingsIconClick: () -> Unit ) { - var showSearchField by remember { mutableStateOf(false) } - TopAppBar( + var showSearchField by remember { mutableStateOf(searchText != "") } + + SmallTopAppBar( title = { if (showSearchField) { SearchField(searchText, onSearchTextChanged) @@ -40,16 +41,16 @@ fun SearchBar( Text("Home") } }, - backgroundColor = MaterialTheme.colorScheme.surfaceVariant, actions = { if (showSearchField) { Text( text = "Cancel", modifier = Modifier .clickable { showSearchField = false } + .padding(horizontal = 6.dp) ) } else { - IconButton(onClick = { showSearchField = true }) { + FilledIconButton(onClick = { showSearchField = true }) { Icon( imageVector = Icons.Filled.Search, contentDescription = null diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/settings/SettingsContent.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/settings/SettingsContent.kt index 6cd329ed5..f17ccff89 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/settings/SettingsContent.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/settings/SettingsContent.kt @@ -1,9 +1,7 @@ import android.annotation.SuppressLint import androidx.compose.foundation.layout.* -import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Home -import androidx.compose.material.icons.filled.Menu import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -13,8 +11,6 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import app.omnivore.omnivore.Routes import app.omnivore.omnivore.ui.auth.LoginViewModel -import app.omnivore.omnivore.ui.home.HomeViewContent -import app.omnivore.omnivore.ui.home.HomeViewModel import com.google.android.gms.auth.api.signin.GoogleSignIn import com.google.android.gms.auth.api.signin.GoogleSignInOptions @@ -27,9 +23,8 @@ fun SettingsView( ) { Scaffold( topBar = { - TopAppBar( + SmallTopAppBar( title = { Text("Settings") }, - backgroundColor = MaterialTheme.colorScheme.surfaceVariant, actions = { IconButton(onClick = { navController.navigate(Routes.Home.route) }) { Icon( From d9b0dea0a9f102fc96b1cc3536f8eea1b2097d55 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 6 Sep 2022 13:20:32 -0700 Subject: [PATCH 3/6] use surfaceVariant color for top app bar container color --- .../main/java/app/omnivore/omnivore/ui/home/SearchBar.kt | 3 +++ .../app/omnivore/omnivore/ui/settings/SettingsContent.kt | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/SearchBar.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/SearchBar.kt index cd1b46b59..9a1e416ce 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/SearchBar.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/SearchBar.kt @@ -41,6 +41,9 @@ fun SearchBar( Text("Home") } }, + colors = TopAppBarDefaults.smallTopAppBarColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant + ), actions = { if (showSearchField) { Text( diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/settings/SettingsContent.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/settings/SettingsContent.kt index f17ccff89..be6a3db98 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/settings/SettingsContent.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/settings/SettingsContent.kt @@ -1,11 +1,14 @@ import android.annotation.SuppressLint +import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Home import androidx.compose.material3.* +import androidx.compose.material3.TopAppBarDefaults.smallTopAppBarColors import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController @@ -25,6 +28,9 @@ fun SettingsView( topBar = { SmallTopAppBar( title = { Text("Settings") }, + colors = TopAppBarDefaults.smallTopAppBarColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant + ), actions = { IconButton(onClick = { navController.navigate(Routes.Home.route) }) { Icon( From ece9c6b9d57c6450e96e2a4749dcb95298c73f16 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 6 Sep 2022 15:35:40 -0700 Subject: [PATCH 4/6] reset search term after search bar is dismissed --- .../main/java/app/omnivore/omnivore/ui/home/SearchBar.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/SearchBar.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/SearchBar.kt index 9a1e416ce..c4d93badc 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/SearchBar.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/SearchBar.kt @@ -49,11 +49,14 @@ fun SearchBar( Text( text = "Cancel", modifier = Modifier - .clickable { showSearchField = false } + .clickable { + onSearchTextChanged("") + showSearchField = false + } .padding(horizontal = 6.dp) ) } else { - FilledIconButton(onClick = { showSearchField = true }) { + IconButton(onClick = { showSearchField = true }) { Icon( imageVector = Icons.Filled.Search, contentDescription = null From c952dd0686e01c95ceca09f4fa51a6dfc8488495 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 6 Sep 2022 20:25:14 -0700 Subject: [PATCH 5/6] track search results order to make sure old queries aren't displayed --- .../omnivore/ui/home/HomeViewModel.kt | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt index fdc4c2671..7b1f47bcf 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt @@ -2,7 +2,9 @@ package app.omnivore.omnivore.ui.home import android.util.Log import androidx.core.net.toUri -import androidx.lifecycle.* +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import app.omnivore.omnivore.Constants import app.omnivore.omnivore.DatastoreKeys import app.omnivore.omnivore.DatastoreRepository @@ -22,6 +24,11 @@ class HomeViewModel @Inject constructor( private var items: List = listOf() private var searchedItems: List = listOf() + // These are used to make sure we handle search result + // responses in the right order + private var searchIdx = 0 + private var receivedIdx = 0 + // Live Data val searchTextLiveData = MutableLiveData("") val itemsLiveData = MutableLiveData>(listOf()) @@ -42,6 +49,8 @@ class HomeViewModel @Inject constructor( fun load(clearPreviousSearch: Boolean = false) { viewModelScope.launch { + val thisSearchIdx = searchIdx + searchIdx += 1 val authToken = getAuthToken() val apolloClient = ApolloClient.Builder() @@ -57,7 +66,18 @@ class HomeViewModel @Inject constructor( ) ).execute() + // Search results aren't guaranteed to return in order so this + // will discard old results that are returned while a user is typing. + // For example if a user types 'Canucks', often the search results + // for 'C' are returned after 'Canucks' because it takes the backend + // much longer to compute. + if (thisSearchIdx in 1..receivedIdx) { + Log.d("loggo", "early return from load function") + return@launch + } + cursor = response.data?.search?.onSearchSuccess?.pageInfo?.endCursor + receivedIdx = thisSearchIdx val itemList = response.data?.search?.onSearchSuccess?.edges ?: listOf() val newItems = itemList.map { From 357faf5947725ae52545f8ad78e4e771a4cf5764 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 6 Sep 2022 21:29:56 -0700 Subject: [PATCH 6/6] fix kotlin string concat error. clear cursor value when search term changes --- .../app/omnivore/omnivore/ui/home/HomeViewModel.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt index 7b1f47bcf..5d4ab6074 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt @@ -48,6 +48,10 @@ class HomeViewModel @Inject constructor( } fun load(clearPreviousSearch: Boolean = false) { + if (clearPreviousSearch) { + cursor = null + } + viewModelScope.launch { val thisSearchIdx = searchIdx searchIdx += 1 @@ -66,20 +70,20 @@ class HomeViewModel @Inject constructor( ) ).execute() + // Search results aren't guaranteed to return in order so this // will discard old results that are returned while a user is typing. // For example if a user types 'Canucks', often the search results // for 'C' are returned after 'Canucks' because it takes the backend // much longer to compute. if (thisSearchIdx in 1..receivedIdx) { - Log.d("loggo", "early return from load function") return@launch } cursor = response.data?.search?.onSearchSuccess?.pageInfo?.endCursor receivedIdx = thisSearchIdx val itemList = response.data?.search?.onSearchSuccess?.edges ?: listOf() - + val newItems = itemList.map { LinkedItem( id = it.node.id, @@ -111,7 +115,7 @@ class HomeViewModel @Inject constructor( var query = "in:inbox sort:saved" if (searchTextLiveData.value != "") { - query.plus(" ${searchTextLiveData.value}") + query = query.plus(" ${searchTextLiveData.value}") } return query