feat(Scout): hide all new filters under a "See All"

This commit is contained in:
Cilly Leang 2026-02-17 22:35:25 +11:00
parent ca3960e279
commit 0741b7951d
Signed by: cilly
GPG key ID: 6500251E087653C9
3 changed files with 104 additions and 16 deletions

View file

@ -3,12 +3,14 @@ package moe.lava.awoocord.scout
import com.discord.utilities.search.query.FilterType import com.discord.utilities.search.query.FilterType
object FilterTypeExtension { object FilterTypeExtension {
lateinit var EXPAND: FilterType
lateinit var BEFORE: FilterType lateinit var BEFORE: FilterType
lateinit var DURING: FilterType lateinit var DURING: FilterType
lateinit var AFTER: FilterType lateinit var AFTER: FilterType
lateinit var SORT: FilterType lateinit var SORT: FilterType
lateinit var EXCLUDE: FilterType lateinit var EXCLUDE: FilterType
lateinit var dates: Array<FilterType> lateinit var dates: Array<FilterType>
lateinit var filters: Array<FilterType>
lateinit var values: Array<FilterType> lateinit var values: Array<FilterType>
} }

View file

@ -8,20 +8,37 @@ import androidx.core.content.res.ResourcesCompat
import com.aliucord.Utils import com.aliucord.Utils
import com.aliucord.annotations.AliucordPlugin import com.aliucord.annotations.AliucordPlugin
import com.aliucord.entities.Plugin import com.aliucord.entities.Plugin
import com.aliucord.patcher.* import com.aliucord.patcher.PreHook
import com.aliucord.patcher.after
import com.aliucord.patcher.before
import com.aliucord.patcher.component1
import com.aliucord.patcher.component2
import com.aliucord.patcher.component3
import com.aliucord.patcher.component4
import com.aliucord.patcher.component5
import com.aliucord.patcher.instead
import com.aliucord.utils.DimenUtils.dp import com.aliucord.utils.DimenUtils.dp
import com.aliucord.utils.RxUtils.subscribe
import com.aliucord.utils.ViewUtils.findViewById import com.aliucord.utils.ViewUtils.findViewById
import com.aliucord.utils.accessField
import com.aliucord.wrappers.ChannelWrapper.Companion.id import com.aliucord.wrappers.ChannelWrapper.Companion.id
import com.discord.BuildConfig import com.discord.BuildConfig
import com.discord.api.channel.* import com.discord.api.channel.Channel
import com.discord.api.channel.ChannelUtils
import com.discord.api.channel.`ChannelUtils$getSortByNameAndType$1`
import com.discord.api.permission.Permission import com.discord.api.permission.Permission
import com.discord.databinding.WidgetSearchSuggestionsItemHasBinding import com.discord.databinding.WidgetSearchSuggestionsItemHasBinding
import com.discord.databinding.WidgetSearchSuggestionsItemSuggestionBinding
import com.discord.models.member.GuildMember import com.discord.models.member.GuildMember
import com.discord.models.user.User import com.discord.models.user.User
import com.discord.restapi.RequiredHeadersInterceptor import com.discord.restapi.RequiredHeadersInterceptor
import com.discord.restapi.RestAPIBuilder import com.discord.restapi.RestAPIBuilder
import com.discord.simpleast.core.parser.* import com.discord.simpleast.core.parser.ParseSpec
import com.discord.stores.* import com.discord.simpleast.core.parser.Parser
import com.discord.simpleast.core.parser.Rule
import com.discord.stores.StoreSearch
import com.discord.stores.StoreSearchInput
import com.discord.stores.StoreStream
import com.discord.utilities.mg_recycler.MGRecyclerDataPayload import com.discord.utilities.mg_recycler.MGRecyclerDataPayload
import com.discord.utilities.mg_recycler.SingleTypePayload import com.discord.utilities.mg_recycler.SingleTypePayload
import com.discord.utilities.rest.RestAPI.AppHeadersProvider import com.discord.utilities.rest.RestAPI.AppHeadersProvider
@ -29,28 +46,44 @@ import com.discord.utilities.search.network.`SearchFetcher$getRestObservable$3`
import com.discord.utilities.search.network.SearchQuery import com.discord.utilities.search.network.SearchQuery
import com.discord.utilities.search.query.FilterType import com.discord.utilities.search.query.FilterType
import com.discord.utilities.search.query.node.QueryNode import com.discord.utilities.search.query.node.QueryNode
import com.discord.utilities.search.query.node.answer.* import com.discord.utilities.search.query.node.answer.ChannelNode
import com.discord.utilities.search.query.node.answer.HasAnswerOption
import com.discord.utilities.search.query.node.answer.HasNode
import com.discord.utilities.search.query.node.answer.UserNode
import com.discord.utilities.search.query.node.content.ContentNode import com.discord.utilities.search.query.node.content.ContentNode
import com.discord.utilities.search.query.node.filter.FilterNode import com.discord.utilities.search.query.node.filter.FilterNode
import com.discord.utilities.search.query.parsing.QueryParser import com.discord.utilities.search.query.parsing.QueryParser
import com.discord.utilities.search.query.parsing.`QueryParser$Companion$getInAnswerRule$1` import com.discord.utilities.search.query.parsing.`QueryParser$Companion$getInAnswerRule$1`
import com.discord.utilities.search.strings.ContextSearchStringProvider
import com.discord.utilities.search.strings.SearchStringProvider import com.discord.utilities.search.strings.SearchStringProvider
import com.discord.utilities.search.suggestion.SearchSuggestionEngine import com.discord.utilities.search.suggestion.SearchSuggestionEngine
import com.discord.utilities.search.suggestion.entries.* import com.discord.utilities.search.suggestion.entries.ChannelSuggestion
import com.discord.utilities.search.suggestion.entries.FilterSuggestion
import com.discord.utilities.search.suggestion.entries.HasSuggestion
import com.discord.utilities.search.suggestion.entries.SearchSuggestion
import com.discord.utilities.search.validation.SearchData import com.discord.utilities.search.validation.SearchData
import com.discord.widgets.search.results.WidgetSearchResults import com.discord.widgets.search.results.WidgetSearchResults
import com.discord.widgets.search.suggestions.WidgetSearchSuggestions import com.discord.widgets.search.suggestions.WidgetSearchSuggestions
import com.discord.widgets.search.suggestions.`WidgetSearchSuggestions$configureUI$1`
import com.discord.widgets.search.suggestions.WidgetSearchSuggestionsAdapter import com.discord.widgets.search.suggestions.WidgetSearchSuggestionsAdapter
import com.franmontiel.persistentcookiejar.PersistentCookieJar import com.franmontiel.persistentcookiejar.PersistentCookieJar
import com.franmontiel.persistentcookiejar.cache.SetCookieCache import com.franmontiel.persistentcookiejar.cache.SetCookieCache
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor
import com.lytefast.flexinput.R import com.lytefast.flexinput.R
import moe.lava.awoocord.scout.api.SearchAPIInterface import moe.lava.awoocord.scout.api.SearchAPIInterface
import moe.lava.awoocord.scout.parsing.* import moe.lava.awoocord.scout.parsing.DateNode
import moe.lava.awoocord.scout.ui.* import moe.lava.awoocord.scout.parsing.SimpleParserRule
import moe.lava.awoocord.scout.parsing.SortNode
import moe.lava.awoocord.scout.parsing.UserIdNode
import moe.lava.awoocord.scout.ui.DatePickerFragment
import moe.lava.awoocord.scout.ui.ScoutResource
import moe.lava.awoocord.scout.ui.ScoutSearchStringProvider
import java.util.regex.Pattern import java.util.regex.Pattern
import b.a.k.b as FormatUtils import b.a.k.b as FormatUtils
private val WidgetSearchSuggestionsAdapter.FilterViewHolder.binding
by accessField<WidgetSearchSuggestionsItemSuggestionBinding>()
@AliucordPlugin() @AliucordPlugin()
@Suppress("unused", "unchecked_cast") @Suppress("unused", "unchecked_cast")
class Scout : Plugin() { class Scout : Plugin() {
@ -58,6 +91,8 @@ class Scout : Plugin() {
lateinit var ssProvider: ScoutSearchStringProvider lateinit var ssProvider: ScoutSearchStringProvider
lateinit var searchApi: SearchAPIInterface lateinit var searchApi: SearchAPIInterface
var optionsExpanded = false
init { init {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
needsResources = true needsResources = true
@ -123,18 +158,21 @@ class Scout : Plugin() {
origFilterTypes = origFilterTypes ?: values origFilterTypes = origFilterTypes ?: values
var nextIdx = values.size var nextIdx = values.size
val EXPAND = constructor.newInstance("EXPAND", nextIdx++) as FilterType
val EXCLUDE = constructor.newInstance("EXCLUDE", nextIdx++) as FilterType val EXCLUDE = constructor.newInstance("EXCLUDE", nextIdx++) as FilterType
val BEFORE = constructor.newInstance("BEFORE", nextIdx++) as FilterType val BEFORE = constructor.newInstance("BEFORE", nextIdx++) as FilterType
val DURING = constructor.newInstance("DURING", nextIdx++) as FilterType val DURING = constructor.newInstance("DURING", nextIdx++) as FilterType
val AFTER = constructor.newInstance("AFTER", nextIdx++) as FilterType val AFTER = constructor.newInstance("AFTER", nextIdx++) as FilterType
val SORT = constructor.newInstance("SORT", nextIdx) as FilterType val SORT = constructor.newInstance("SORT", nextIdx) as FilterType
FilterTypeExtension.EXPAND = EXPAND
FilterTypeExtension.EXCLUDE = EXCLUDE FilterTypeExtension.EXCLUDE = EXCLUDE
FilterTypeExtension.BEFORE = BEFORE FilterTypeExtension.BEFORE = BEFORE
FilterTypeExtension.DURING = DURING FilterTypeExtension.DURING = DURING
FilterTypeExtension.AFTER = AFTER FilterTypeExtension.AFTER = AFTER
FilterTypeExtension.SORT = SORT FilterTypeExtension.SORT = SORT
FilterTypeExtension.dates = arrayOf(BEFORE, DURING, AFTER) FilterTypeExtension.dates = arrayOf(BEFORE, DURING, AFTER)
FilterTypeExtension.values = arrayOf(EXCLUDE, BEFORE, DURING, AFTER, SORT) FilterTypeExtension.values = arrayOf(EXPAND, EXCLUDE, BEFORE, DURING, AFTER, SORT)
FilterTypeExtension.filters = arrayOf(EXCLUDE, BEFORE, DURING, AFTER, SORT)
val newValues = values.toMutableList() val newValues = values.toMutableList()
newValues.addAll(FilterTypeExtension.values) newValues.addAll(FilterTypeExtension.values)
@ -544,7 +582,8 @@ class Scout : Plugin() {
// Patch formatting utils to use our custom lowercase strings // Patch formatting utils to use our custom lowercase strings
// This is called by FilterViewHolder.onConfigure, using the results from getAnswerText and getFilterText // This is called by FilterViewHolder.onConfigure, using the results from getAnswerText and getFilterText
patcher.patch( patcher.patch(
FormatUtils::class.java.getDeclaredMethod("c", FormatUtils::class.java.getDeclaredMethod(
"c",
Resources::class.java, Resources::class.java,
Int::class.javaPrimitiveType!!, Int::class.javaPrimitiveType!!,
Array::class.java, Array::class.java,
@ -565,20 +604,63 @@ class Scout : Plugin() {
} }
) )
// Patch to key filters properly for smoother recycling
// Thank u discord for keying every filter type the same thing!! /s
patcher.instead<WidgetSearchSuggestionsAdapter.Companion>(
"getFilterItem",
FilterSuggestion::class.java,
) { (_, suggestion: FilterSuggestion) ->
SingleTypePayload(suggestion, suggestion.filterType.name, 2) // 2 = WidgetSearchSuggestionsAdapter.TYPE_FILTER
}
// Patch to manually configure expander, need to do this to update the suggestions widget
patcher.before<WidgetSearchSuggestionsAdapter.FilterViewHolder>(
"onConfigure",
Int::class.javaPrimitiveType!!,
MGRecyclerDataPayload::class.java,
) { (param, _: Int, payload: SingleTypePayload<FilterSuggestion>) ->
val suggestion = payload.data
if (suggestion.filterType != FilterTypeExtension.EXPAND) {
return@before
}
param.result = null
val sampleText = binding.b
val layout = binding.c
val filterText = binding.d
val icon = binding.e
layout.setOnClickListener {
val onFilter = adapter.onFilterClicked as `WidgetSearchSuggestions$configureUI$1`
val widget = onFilter.`this$0`
optionsExpanded = true
WidgetSearchSuggestions.Model.Companion!!.get(ContextSearchStringProvider(context)).z().subscribe {
WidgetSearchSuggestions.`access$configureUI`(widget, this)
}
}
sampleText.text = null
filterText.text = ssProvider.expandFilterString
val drawable = R.e.ic_chevron_right_primary_300_12dp
icon.setImageDrawable(ResourcesCompat.getDrawable(context.resources, drawable, null))
}
// Patch to add our new filters into the initial suggestions // Patch to add our new filters into the initial suggestions
patcher.after<SearchSuggestionEngine>( patcher.after<SearchSuggestionEngine>(
"getFilterSuggestions", "getFilterSuggestions",
CharSequence::class.java, CharSequence::class.java,
SearchStringProvider::class.java, SearchStringProvider::class.java,
Boolean::class.javaPrimitiveType!!, Boolean::class.javaPrimitiveType!!,
) { param -> ) { (param, query: CharSequence) ->
val query = param.args[0] as CharSequence
val res = (param.result as List<SearchSuggestion>).toMutableList() val res = (param.result as List<SearchSuggestion>).toMutableList()
for (type in FilterTypeExtension.values) {
val st = ssProvider.stringFor(type) + ":"
if (st.contains(query)) if (optionsExpanded || query != "") {
res.add(FilterSuggestion(type)) for (type in FilterTypeExtension.filters) {
val st = ssProvider.stringFor(type) + ":"
if (st.contains(query))
res.add(FilterSuggestion(type))
}
} else {
res.add(FilterSuggestion(FilterTypeExtension.EXPAND))
} }
param.result = res.toList() param.result = res.toList()
} }
@ -594,6 +676,8 @@ class Scout : Plugin() {
} }
patcher.after<WidgetSearchSuggestions>("onViewBound", View::class.java) { patcher.after<WidgetSearchSuggestions>("onViewBound", View::class.java) {
// Being a bit sneaky and reset the expanded flag here
optionsExpanded = false
view?.run { view?.run {
fitsSystemWindows = false fitsSystemWindows = false
setPadding(paddingLeft, 16.dp, paddingRight, paddingBottom) setPadding(paddingLeft, 16.dp, paddingRight, paddingBottom)

View file

@ -41,6 +41,8 @@ class ScoutSearchStringProvider(private val context: Context) {
get() = getString("sort").decapitalise(context) get() = getString("sort").decapitalise(context)
val sortOldString: String val sortOldString: String
get() = getString("search_oldest_short").decapitalise(context) get() = getString("search_oldest_short").decapitalise(context)
val expandFilterString: String
get() = getString("friends_pending_request_expand")
// Not localised // Not localised
val hasPollString: String val hasPollString: String