feat: Toggle display of search operators with toolbar action (#836)
Default to hiding the search operators, and provide a new toolbar icon (always visible) to show them. The toolbar icon is displayed with a badge if any operators are present. Adjust the operator display to three horizontal scrolling rows, to further limit the maximum amount of vertical space the operators use.
This commit is contained in:
parent
5d574d4d76
commit
01831474dc
|
@ -105,7 +105,7 @@
|
|||
line="388"
|
||||
column="28"/>
|
||||
<location
|
||||
file="${:core:activity*buildDir}/generated/res/resValues/orangeFdroid/debug/values/gradleResValues.xml"
|
||||
file="${:core:activity*buildDir}/generated/res/resValues/blueFdroid/debug/values/gradleResValues.xml"
|
||||
line="7"
|
||||
column="5"
|
||||
message="This definition does not require arguments"/>
|
||||
|
@ -767,7 +767,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="282"
|
||||
line="283"
|
||||
column="5"/>
|
||||
</issue>
|
||||
|
||||
|
@ -778,7 +778,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="333"
|
||||
line="334"
|
||||
column="5"/>
|
||||
</issue>
|
||||
|
||||
|
@ -789,7 +789,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="452"
|
||||
line="453"
|
||||
column="5"/>
|
||||
</issue>
|
||||
|
||||
|
@ -800,7 +800,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="628"
|
||||
line="629"
|
||||
column="5"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1196,7 +1196,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/donottranslate.xml"
|
||||
line="273"
|
||||
line="275"
|
||||
column="19"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1207,7 +1207,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/donottranslate.xml"
|
||||
line="278"
|
||||
line="280"
|
||||
column="19"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1372,7 +1372,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="198"
|
||||
line="199"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1383,7 +1383,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="219"
|
||||
line="220"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1394,7 +1394,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="220"
|
||||
line="221"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1405,7 +1405,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="243"
|
||||
line="244"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1416,7 +1416,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="272"
|
||||
line="273"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1427,7 +1427,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="343"
|
||||
line="344"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1438,7 +1438,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="383"
|
||||
line="384"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1449,7 +1449,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="419"
|
||||
line="420"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1460,7 +1460,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="422"
|
||||
line="423"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1471,7 +1471,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="423"
|
||||
line="424"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1482,7 +1482,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="424"
|
||||
line="425"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1493,7 +1493,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="425"
|
||||
line="426"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1504,7 +1504,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="426"
|
||||
line="427"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1515,7 +1515,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="438"
|
||||
line="439"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1526,7 +1526,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="439"
|
||||
line="440"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1537,7 +1537,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="490"
|
||||
line="491"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1548,7 +1548,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="491"
|
||||
line="492"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1559,7 +1559,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="502"
|
||||
line="503"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1570,7 +1570,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="503"
|
||||
line="504"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1581,7 +1581,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="504"
|
||||
line="505"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1592,7 +1592,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="507"
|
||||
line="508"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1603,7 +1603,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="545"
|
||||
line="546"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1614,7 +1614,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="586"
|
||||
line="587"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1625,7 +1625,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="592"
|
||||
line="593"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1636,7 +1636,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="620"
|
||||
line="621"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1647,7 +1647,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="646"
|
||||
line="647"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import android.view.Menu
|
|||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.view.MenuProvider
|
||||
|
@ -70,6 +71,7 @@ import app.pachli.components.search.adapter.SearchPagerAdapter
|
|||
import app.pachli.core.activity.BottomSheetActivity
|
||||
import app.pachli.core.common.extensions.hide
|
||||
import app.pachli.core.common.extensions.show
|
||||
import app.pachli.core.common.extensions.toggleVisibility
|
||||
import app.pachli.core.common.extensions.viewBinding
|
||||
import app.pachli.core.common.extensions.visible
|
||||
import app.pachli.core.network.Server
|
||||
|
@ -98,7 +100,11 @@ import app.pachli.databinding.SearchOperatorDateDialogBinding
|
|||
import app.pachli.databinding.SearchOperatorFromDialogBinding
|
||||
import app.pachli.databinding.SearchOperatorWhereLocationDialogBinding
|
||||
import com.github.michaelbull.result.get
|
||||
import com.google.android.material.badge.BadgeDrawable
|
||||
import com.google.android.material.badge.BadgeUtils
|
||||
import com.google.android.material.badge.ExperimentalBadgeUtils
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.datepicker.CalendarConstraints
|
||||
import com.google.android.material.datepicker.DateValidatorPointBackward
|
||||
import com.google.android.material.datepicker.MaterialDatePicker
|
||||
|
@ -125,6 +131,9 @@ class SearchActivity :
|
|||
|
||||
private lateinit var searchView: SearchView
|
||||
|
||||
val showFilterIcon: Boolean
|
||||
get() = viewModel.availableOperators.value.isNotEmpty()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(binding.root)
|
||||
|
@ -156,6 +165,7 @@ class SearchActivity :
|
|||
* Binds the initial search operator chips UI and updates as the search
|
||||
* operators change.
|
||||
*/
|
||||
@OptIn(ExperimentalBadgeUtils::class)
|
||||
private fun bindOperators() {
|
||||
val viewDataToChip: Map<Class<out SearchOperatorViewData<SearchOperator>>, Chip> = mapOf(
|
||||
DateOperatorViewData::class.java to binding.chipDate,
|
||||
|
@ -170,6 +180,18 @@ class SearchActivity :
|
|||
WhereOperatorViewData::class.java to binding.chipWhere,
|
||||
)
|
||||
|
||||
// Chips are initially hidden, toggled by the "filter" button
|
||||
binding.chipsFilter.hide()
|
||||
binding.chipsFilter2.hide()
|
||||
binding.chipsFilter3.hide()
|
||||
|
||||
// Badge to draw on the filter button if any filters are active.
|
||||
val filterBadgeDrawable = BadgeDrawable.create(this).apply {
|
||||
text = "!"
|
||||
backgroundColor = MaterialColors.getColor(binding.toolbar, com.google.android.material.R.attr.colorPrimary)
|
||||
}
|
||||
BadgeUtils.attachBadgeDrawable(filterBadgeDrawable, binding.toolbar, R.id.action_filter_search)
|
||||
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
launch {
|
||||
|
@ -189,17 +211,28 @@ class SearchActivity :
|
|||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.availableOperators.collectLatest {
|
||||
invalidateOptionsMenu()
|
||||
setSearchViewWidth(showFilterIcon)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.operatorViewData.collectLatest { operators ->
|
||||
var showFilterBadgeDrawable = false
|
||||
|
||||
operators.forEach { viewData ->
|
||||
viewDataToChip[viewData::class.java]?.let { chip ->
|
||||
showFilterBadgeDrawable = showFilterBadgeDrawable or (viewData.operator.choice != null)
|
||||
chip.isChecked = viewData.operator.choice != null
|
||||
chip.setCloseIconVisible(viewData.operator.choice != null)
|
||||
chip.text = viewData.chipLabel(this@SearchActivity)
|
||||
}
|
||||
|
||||
viewModel.search()
|
||||
}
|
||||
|
||||
filterBadgeDrawable.setVisible(showFilterBadgeDrawable)
|
||||
viewModel.search()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -933,15 +966,35 @@ class SearchActivity :
|
|||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
super.onCreateMenu(menu, menuInflater)
|
||||
menuInflater.inflate(R.menu.search_toolbar, menu)
|
||||
|
||||
menu.findItem(R.id.action_filter_search)?.apply {
|
||||
icon = makeIcon(this@SearchActivity, GoogleMaterial.Icon.gmd_tune, IconicsSize.dp(20))
|
||||
}
|
||||
|
||||
val searchViewMenuItem = menu.findItem(R.id.action_search)
|
||||
searchViewMenuItem.expandActionView()
|
||||
searchView = searchViewMenuItem.actionView as SearchView
|
||||
bindSearchView()
|
||||
}
|
||||
|
||||
override fun onPrepareMenu(menu: Menu) {
|
||||
menu.findItem(R.id.action_filter_search)?.apply {
|
||||
isVisible = showFilterIcon
|
||||
}
|
||||
return super<BottomSheetActivity>.onPrepareMenu(menu)
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
super.onMenuItemSelected(menuItem)
|
||||
return false
|
||||
return when (menuItem.itemId) {
|
||||
R.id.action_filter_search -> {
|
||||
binding.chipsFilter.toggleVisibility()
|
||||
binding.chipsFilter2.toggleVisibility()
|
||||
binding.chipsFilter3.toggleVisibility()
|
||||
true
|
||||
}
|
||||
|
||||
else -> super.onMenuItemSelected(menuItem)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPageTitle(position: Int): CharSequence {
|
||||
|
@ -965,6 +1018,27 @@ class SearchActivity :
|
|||
searchView.setIconifiedByDefault(false)
|
||||
searchView.setSearchableInfo((getSystemService(Context.SEARCH_SERVICE) as? SearchManager)?.getSearchableInfo(componentName))
|
||||
|
||||
setSearchViewWidth(showFilterIcon)
|
||||
|
||||
// Keep text that was entered also when switching to a different tab (before the search is executed)
|
||||
searchView.setOnQueryTextListener(this)
|
||||
searchView.setQuery(viewModel.currentSearchFieldContent ?: "", false)
|
||||
|
||||
// Only focus if the query is empty. This ensures that if the user is returning
|
||||
// to the search results after visiting a result the full list is available,
|
||||
// instead of being obscured by the keyboard.
|
||||
if (viewModel.currentQuery.isBlank()) searchView.requestFocus()
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute and set the width of [searchView].
|
||||
*
|
||||
* @param showingFilterIcon True if the filter icon is showing and the width should
|
||||
* be adjusted to account for this.
|
||||
*/
|
||||
private fun setSearchViewWidth(showingFilterIcon: Boolean) {
|
||||
if (!this::searchView.isInitialized) return
|
||||
|
||||
// SearchView has a bug. If it's displayed 'app:showAsAction="always"' it's too wide,
|
||||
// pushing other icons (including the options menu '...' icon) off the edge of the
|
||||
// screen.
|
||||
|
@ -986,20 +1060,11 @@ class SearchActivity :
|
|||
// It appears to be impossible to override this behaviour on API level < 33.
|
||||
//
|
||||
// SearchView does allow you to specify the maximum width. So take the screen width,
|
||||
// subtract 48dp * 2 (for the menu icon and back icon on either side), convert to pixels,
|
||||
// and use that.
|
||||
// subtract 48dp * iconCount (for the menu, filter, and back icons), convert to pixels, and use that.
|
||||
val iconCount = if (showingFilterIcon) 3 else 2
|
||||
val pxScreenWidth = resources.displayMetrics.widthPixels
|
||||
val pxBuffer = ((48 * 2) * resources.displayMetrics.density).toInt()
|
||||
val pxBuffer = ((48 * iconCount) * resources.displayMetrics.density).toInt()
|
||||
searchView.maxWidth = pxScreenWidth - pxBuffer
|
||||
|
||||
// Keep text that was entered also when switching to a different tab (before the search is executed)
|
||||
searchView.setOnQueryTextListener(this)
|
||||
searchView.setQuery(viewModel.currentSearchFieldContent ?: "", false)
|
||||
|
||||
// Only focus if the query is empty. This ensures that if the user is returning
|
||||
// to the search results after visiting a result the full list is available,
|
||||
// instead of being obscured by the keyboard.
|
||||
if (viewModel.currentQuery.isBlank()) searchView.requestFocus()
|
||||
}
|
||||
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
|
|
|
@ -36,6 +36,20 @@ import app.pachli.components.search.adapter.SearchPagingSourceFactory
|
|||
import app.pachli.core.accounts.AccountManager
|
||||
import app.pachli.core.data.repository.ServerRepository
|
||||
import app.pachli.core.database.model.AccountEntity
|
||||
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_BY_DATE
|
||||
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_FROM
|
||||
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_AUDIO
|
||||
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_EMBED
|
||||
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_IMAGE
|
||||
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_LINK
|
||||
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_MEDIA
|
||||
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_POLL
|
||||
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_VIDEO
|
||||
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IN_LIBRARY
|
||||
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IN_PUBLIC
|
||||
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_REPLY
|
||||
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_SENSITIVE
|
||||
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_LANGUAGE
|
||||
import app.pachli.core.network.model.DeletedStatus
|
||||
import app.pachli.core.network.model.Poll
|
||||
import app.pachli.core.network.model.Status
|
||||
|
@ -47,7 +61,9 @@ import app.pachli.viewdata.StatusViewData
|
|||
import at.connyduck.calladapter.networkresult.NetworkResult
|
||||
import at.connyduck.calladapter.networkresult.fold
|
||||
import at.connyduck.calladapter.networkresult.onFailure
|
||||
import com.github.michaelbull.result.mapBoth
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import io.github.z4kn4fein.semver.constraints.toConstraint
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.async
|
||||
|
@ -118,6 +134,68 @@ class SearchViewModel @Inject constructor(
|
|||
null,
|
||||
)
|
||||
|
||||
/**
|
||||
* Set of operators the server supports.
|
||||
*
|
||||
* Empty set if the server does not support any operators.
|
||||
*/
|
||||
val availableOperators = serverRepository.flow.map { result ->
|
||||
result.mapBoth(
|
||||
{ server ->
|
||||
buildSet {
|
||||
val constraint100 = ">=1.0.0".toConstraint()
|
||||
val canHasMedia = server.can(ORG_JOINMASTODON_SEARCH_QUERY_HAS_MEDIA, constraint100)
|
||||
val canHasImage = server.can(ORG_JOINMASTODON_SEARCH_QUERY_HAS_IMAGE, constraint100)
|
||||
val canHasVideo = server.can(ORG_JOINMASTODON_SEARCH_QUERY_HAS_VIDEO, constraint100)
|
||||
val canHasAudio = server.can(ORG_JOINMASTODON_SEARCH_QUERY_HAS_AUDIO, constraint100)
|
||||
if (canHasMedia || canHasImage || canHasVideo || canHasAudio) {
|
||||
add(HasMediaOperator())
|
||||
}
|
||||
|
||||
if (server.can(ORG_JOINMASTODON_SEARCH_QUERY_BY_DATE, constraint100)) {
|
||||
add(DateOperator())
|
||||
}
|
||||
|
||||
if (server.can(ORG_JOINMASTODON_SEARCH_QUERY_FROM, constraint100)) {
|
||||
add(FromOperator())
|
||||
}
|
||||
|
||||
if (server.can(ORG_JOINMASTODON_SEARCH_QUERY_LANGUAGE, constraint100)) {
|
||||
add(LanguageOperator())
|
||||
}
|
||||
|
||||
if (server.can(ORG_JOINMASTODON_SEARCH_QUERY_HAS_LINK, constraint100)) {
|
||||
add(HasLinkOperator())
|
||||
}
|
||||
if (server.can(ORG_JOINMASTODON_SEARCH_QUERY_HAS_EMBED, constraint100)) {
|
||||
add(HasEmbedOperator())
|
||||
}
|
||||
|
||||
if (server.can(ORG_JOINMASTODON_SEARCH_QUERY_HAS_POLL, constraint100)) {
|
||||
add(HasPollOperator())
|
||||
}
|
||||
if (server.can(ORG_JOINMASTODON_SEARCH_QUERY_IS_REPLY, constraint100)) {
|
||||
add(IsReplyOperator())
|
||||
}
|
||||
if (server.can(ORG_JOINMASTODON_SEARCH_QUERY_IS_SENSITIVE, constraint100)) {
|
||||
add(IsSensitiveOperator())
|
||||
}
|
||||
|
||||
val canInLibrary = server.can(ORG_JOINMASTODON_SEARCH_QUERY_IN_LIBRARY, constraint100)
|
||||
val canInPublic = server.can(ORG_JOINMASTODON_SEARCH_QUERY_IN_PUBLIC, constraint100)
|
||||
if (canInLibrary || canInPublic) add(WhereOperator())
|
||||
}
|
||||
},
|
||||
{
|
||||
emptySet()
|
||||
},
|
||||
)
|
||||
}.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(5000),
|
||||
emptySet(),
|
||||
)
|
||||
|
||||
private val loadedStatuses: MutableList<StatusViewData> = mutableListOf()
|
||||
|
||||
private val statusesPagingSourceFactory = SearchPagingSourceFactory(mastodonApi, SearchType.Status, loadedStatuses) {
|
||||
|
@ -162,7 +240,7 @@ class SearchViewModel @Inject constructor(
|
|||
* with [viewData].
|
||||
*/
|
||||
fun <T : SearchOperator> replaceOperator(viewData: SearchOperatorViewData<T>) = _operatorViewData.update { operators ->
|
||||
operators.find { it.javaClass == viewData.javaClass }?.let { operators - it + viewData } ?: (operators + viewData)
|
||||
operators.find { it.javaClass == viewData.javaClass }?.let { operators - it + viewData } ?: operators
|
||||
}
|
||||
|
||||
fun search() {
|
||||
|
|
|
@ -21,100 +21,141 @@
|
|||
app:layout_scrollFlags="scroll|snap|enterAlways"
|
||||
app:navigationIcon="?attr/homeAsUpIndicator" />
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/chipsFilter"
|
||||
android:layout_width="match_parent"
|
||||
<HorizontalScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="?listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?listPreferredItemPaddingEnd"
|
||||
app:layout_scrollFlags="scroll|snap|enterAlways"
|
||||
android:animateLayoutChanges="true">
|
||||
android:scrollbars="none">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipFrom"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/chipsFilter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_from_all" />
|
||||
android:paddingStart="?listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?listPreferredItemPaddingEnd"
|
||||
app:singleLine="true"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipDate"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipFrom"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_from_all" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipDate"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_date_all" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipLanguage"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_language_all" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipWhere"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_where_all" />
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
</HorizontalScrollView>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_scrollFlags="scroll|snap|enterAlways"
|
||||
android:scrollbars="none">
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/chipsFilter2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_date_all" />
|
||||
android:paddingStart="?listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?listPreferredItemPaddingEnd"
|
||||
app:singleLine="true"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipLanguage"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chip_has_media"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIcon="@drawable/ic_attach_file_24dp"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_attachment_all" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chip_has_poll"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIcon="@drawable/ic_attach_file_24dp"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_poll_all" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chip_has_link"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIcon="@drawable/ic_link_24"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_link_all" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chip_has_embed"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_embed_all" />
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
</HorizontalScrollView>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_scrollFlags="scroll|snap|enterAlways"
|
||||
android:scrollbars="none">
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/chipsFilter3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_language_all" />
|
||||
android:paddingStart="?listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?listPreferredItemPaddingEnd"
|
||||
app:singleLine="true"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chip_has_media"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIcon="@drawable/ic_attach_file_24dp"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_attachment_all" />
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chip_is_reply"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIcon="@drawable/ic_reply_all_24dp"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_replies_all" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chip_is_reply"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIcon="@drawable/ic_reply_all_24dp"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_replies_all" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chip_is_sensitive"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIcon="@drawable/ic_eye_24dp"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_sensitive_all" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chip_has_poll"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIcon="@drawable/ic_attach_file_24dp"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_poll_all" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chip_has_embed"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_embed_all" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chip_has_link"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIcon="@drawable/ic_link_24"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_link_all" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipWhere"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_where_all" />
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chip_is_sensitive"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:chipIcon="@drawable/ic_eye_24dp"
|
||||
app:chipIconEnabled="true"
|
||||
android:text="@string/search_operator_sensitive_all" />
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
</HorizontalScrollView>
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<menu xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
|
@ -8,5 +9,12 @@
|
|||
android:icon="@android:drawable/ic_menu_search"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||
android:actionLayout="@layout/search_view"
|
||||
app:showAsAction="always" />
|
||||
app:showAsAction="always"
|
||||
tools:ignore="AlwaysShowAction" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_filter_search"
|
||||
android:title="@string/action_filter_search"
|
||||
app:showAsAction="always"
|
||||
tools:ignore="AlwaysShowAction" />
|
||||
</menu>
|
||||
|
|
|
@ -165,6 +165,7 @@
|
|||
<string name="action_accept">Accept</string>
|
||||
<string name="action_reject">Reject</string>
|
||||
<string name="action_search">Search</string>
|
||||
<string name="action_filter_search">Filter search</string>
|
||||
<string name="action_access_drafts">Drafts</string>
|
||||
<string name="action_access_scheduled_posts">Scheduled posts</string>
|
||||
<string name="action_toggle_visibility">Post visibility</string>
|
||||
|
|
|
@ -30,3 +30,11 @@ fun View.hide() {
|
|||
fun View.visible(visible: Boolean, or: Int = View.GONE) {
|
||||
this.visibility = if (visible) View.VISIBLE else or
|
||||
}
|
||||
|
||||
fun View.toggleVisibility() {
|
||||
when (this.visibility) {
|
||||
View.GONE -> this.show()
|
||||
View.INVISIBLE -> this.show()
|
||||
View.VISIBLE -> this.hide()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/styles.xml"
|
||||
line="134"
|
||||
line="137"
|
||||
column="42"/>
|
||||
</issue>
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
|||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/styles.xml"
|
||||
line="135"
|
||||
line="138"
|
||||
column="43"/>
|
||||
</issue>
|
||||
|
||||
|
|
Loading…
Reference in New Issue